/*
* Copyright (C) 2013 Fairphone Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fairphone.launcher.gappsinstaller;
import com.stericson.RootTools.RootTools;
import com.stericson.RootTools.exceptions.RootDeniedException;
import com.stericson.RootTools.execution.CommandCapture;
import com.stericson.RootTools.execution.Shell;
import org.fairphone.launcher.R;
import org.fairphone.launcher.rsa.utils.RSAUtils;
import org.fairphone.widgets.gapps.GoogleAppsInstallerWidget;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Environment;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.util.concurrent.TimeoutException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class GappsInstallerHelper {
private static final String GOOGLE_APPS_DOWNLOAD_ID = "org.fairphone.launcher.gapps.DOWNLOAD_ID";
public static final String GAPPS_ACTION_DISCLAIMER = "org.fairphone.launcher.gapps.DISCLAIMER";
public static final String GAPPS_ACTION_DOWNLOAD_CONFIGURATION_FILE = "org.fairphone.launcher.gapps.START_DONWLOAD_CONFIGURATION";
public static final String GOOGLE_APPS_INSTALL_DOWNLOAD_CANCEL = "org.fairphone.launcher.gapps.START_DOWNLOAD_CANCEL";
public static final String GOOGLE_APPS_INSTALL_REBOOT = "org.fairphone.launcher.gapps.REBOOT";
public static final String PREFS_GOOGLE_APPS_INSTALLER_DATA = "FAIRPHONE_GOOGLE_APPS_INSTALLER_DATA";
public static final String GOOGLE_APPS_INSTALLER_STATE = "org.fairphone.launcher.gapps.WIDGET_STATE";
public static final String GOOGLE_APPS_INSTALLER_PROGRESS = "org.fairphone.launcher.gapps.WIDGET_SEEKBAR_PROGRESS";
public static final String GOOGLE_APPS_INSTALLER_PROGRESS_MAX = "org.fairphone.launcher.gapps.WIDGET_SEEKBAR_PROGRESS_MAX";
public static final String GAPPS_ACTION_GO_PERMISSIONS = "org.fairphone.launcher.gaps.GAPPS_ACTION_GO_PERMISSIONS";
public static final int GAPPS_STATES_INITIAL = 0;
public static final int GAPPS_STATES_DOWNLOAD_CONFIGURATION_FILE = 1;
public static final int GAPPS_STATES_DOWNLOAD_GOOGLE_APPS_FILE = 2;
public static final int GAPPS_STATES_EXTRACT_FILES = 3;
public static final int GAPPS_STATES_PERMISSION_CHECK = 4;
public static final int GAPPS_STATE_INSTALLATION = 5;
public static final int GAPPS_REBOOT_STATE = 6;
public static final int GAPPS_INSTALLED_STATE = 7;
public static final int GAPPS_INSTALLATION_FAILED_STATE = 8;
public static final int GAPPS_DOWNLOAD_FAILED_STATE = 9;
protected static final String TAG = GappsInstallerHelper.class
.getSimpleName();
private static String DOWNLOAD_PATH = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getAbsolutePath();
private static String RECOVERY_PATH = "sdcard/Download/";
private static String ZIP_CONTENT_PATH = "/googleapps/";
private DownloadManager mDownloadManager;
private Context mContext;
private SharedPreferences mSharedPrefs;
private DownloadBroadCastReceiver mDownloadBroacastReceiver;
private long mConfigFileDownloadId;
private long mGappsFileDownloadId;
private String mMD5hash;
public GappsInstallerHelper(Context context) {
mContext = context;
mSharedPrefs = mContext.getSharedPreferences(
PREFS_GOOGLE_APPS_INSTALLER_DATA, Context.MODE_PRIVATE);
resume();
int currentState = getInstallerState();
if (currentState == GAPPS_REBOOT_STATE) {
updateWidgetState(GAPPS_STATES_PERMISSION_CHECK);
}
if (currentState != GAPPS_STATE_INSTALLATION &&
currentState != GAPPS_INSTALLED_STATE) {
// clean files that must be rechecked
forceCleanUnzipDirectory();
forceCleanConfigurationFile();
updateInstallerState(GAPPS_STATES_INITIAL);
}
checkGappsAreInstalled();
}
private void checkGappsAreInstalled() {
File f = new File("/system/app/OneTimeInitializer.apk");
if (f.exists()) {
updateWidgetState(GAPPS_INSTALLED_STATE);
}
}
public void resume() {
// setup the download manager
setupDownloadManager();
// setup the states broadcasts receivers
setupTheStatesBroadCasts();
}
public void pause() {
// clear the download manager
clearDownloadManager();
// clean the broadcast receivers
clearBroadcastReceivers();
}
private int getCurrentState() {
return mSharedPrefs.getInt(GOOGLE_APPS_INSTALLER_STATE,
GAPPS_STATES_INITIAL);
}
private void setupDownloadManager() {
mDownloadManager = (DownloadManager) mContext
.getSystemService(Context.DOWNLOAD_SERVICE);
mDownloadBroacastReceiver = new DownloadBroadCastReceiver();
mContext.registerReceiver(mDownloadBroacastReceiver, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
private void clearDownloadManager() {
mContext.unregisterReceiver(mDownloadBroacastReceiver);
mDownloadBroacastReceiver = null;
}
private boolean isWiFiEnabled() {
ConnectivityManager manager = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isWifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
.isConnectedOrConnecting();
return isWifi;
}
private boolean hasAlreadyDownloadedZipFile(String mMD5hash, String filename) {
File file = new File(DOWNLOAD_PATH + "/" + filename);
return GappsInstallerHelper.checkMD5(mMD5hash, file);
}
private String[] getGappsUrlFromConfigFile(String filePath) {
String[] result = new String[2];
File configFile = new File(filePath);
try {
BufferedReader br = new BufferedReader(new FileReader(configFile));
result[0] = br.readLine();
result[1] = br.readLine();
br.close();
} catch (FileNotFoundException e) {
Log.e(TAG, "Configuration file not find", e);
result = null;
} catch (IOException e) {
Log.e(TAG, "Configuration file could not be read", e);
result = null;
}
return result;
}
private void deleteFile(String file, String location) {
File f = new File(location + file);
if (f.exists()) {
deleteRecursive(f);
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
for (File child : fileOrDirectory.listFiles()) {
deleteRecursive(child);
}
}
fileOrDirectory.delete();
}
private void forceCleanConfigurationFile() {
if (mConfigFileDownloadId != 0) {
mDownloadManager.remove(mConfigFileDownloadId);
}
String configFileName = mContext.getResources().getString(
R.string.gapps_installer_config_file);
String configFileZip = mContext.getResources().getString(
R.string.gapps_installer_zip);
String configFileCfg = mContext.getResources().getString(
R.string.gapps_installer_cfg);
String configFileSig = mContext.getResources().getString(
R.string.gapps_installer_sig);
deleteFile("/" + configFileName + configFileZip, DOWNLOAD_PATH);
deleteFile("/" + configFileName + configFileCfg, DOWNLOAD_PATH);
deleteFile("/" + configFileName + configFileSig, DOWNLOAD_PATH);
}
private void forceCleanGappsZipFile() {
long downloadID = mSharedPrefs.getLong(GOOGLE_APPS_DOWNLOAD_ID, 0);
if (downloadID != 0) {
mDownloadManager.remove(downloadID);
}
String gappsFileName = mContext.getResources().getString(
R.string.gapps_installer_filename);
deleteFile("/" + gappsFileName, DOWNLOAD_PATH);
}
private void forceCleanUnzipDirectory() {
deleteFile(ZIP_CONTENT_PATH, DOWNLOAD_PATH);
}
private BroadcastReceiver mBCastDisclaimer;
private BroadcastReceiver mBCastDownloadConfiguration;
private BroadcastReceiver mBCastInstallDownloadCancel;
private BroadcastReceiver mBCastGoPermissions;
private BroadcastReceiver mBCastGappsInstallReboot;
private void setupTheStatesBroadCasts() {
// launching the application
mBCastDisclaimer = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Resources resources = mContext.getResources();
AlertDialog disclaimerDialog = new AlertDialog.Builder(mContext)
.create();
disclaimerDialog.setTitle(resources
.getText(R.string.google_apps_disclaimer_title));
// Setting Dialog Message
disclaimerDialog.setMessage(resources
.getText(R.string.google_apps_disclaimer_description));
disclaimerDialog
.setButton(
AlertDialog.BUTTON_POSITIVE,
resources
.getString(R.string.google_apps_disclaimer_agree),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
Intent startDownloadOkIntent = new Intent();
startDownloadOkIntent
.setAction(GappsInstallerHelper.GAPPS_ACTION_DOWNLOAD_CONFIGURATION_FILE);
mContext.sendBroadcast(startDownloadOkIntent);
}
});
disclaimerDialog.setButton(AlertDialog.BUTTON_NEGATIVE,
resources.getString(android.R.string.cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
updateWidgetState(GAPPS_STATES_INITIAL);
}
});
disclaimerDialog.show();
}
};
mContext.registerReceiver(mBCastDisclaimer, new IntentFilter(
GAPPS_ACTION_DISCLAIMER));
mBCastDownloadConfiguration = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// clean the configuration files
forceCleanConfigurationFile();
if (isWiFiEnabled()) {
String url = mContext.getResources().getString(
R.string.gapps_installer_download_url);
String configFileName = mContext.getResources().getString(
R.string.gapps_installer_config_file);
String configFileZip = mContext.getResources().getString(
R.string.gapps_installer_zip);
Request request = createDownloadRequest(url, configFileName + configFileZip);
mConfigFileDownloadId = mDownloadManager.enqueue(request);
updateWidgetState(GAPPS_STATES_DOWNLOAD_CONFIGURATION_FILE);
} else {
AlertDialog disclaimerDialog = new AlertDialog.Builder(
mContext).create();
Resources resources = mContext.getResources();
disclaimerDialog.setTitle(resources
.getText(R.string.google_apps_connection_title));
// Setting Dialog Message
disclaimerDialog
.setMessage(resources
.getText(R.string.google_apps_connection_description));
disclaimerDialog.setButton(AlertDialog.BUTTON_POSITIVE,
resources.getString(android.R.string.ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
updateWidgetState(GAPPS_STATES_INITIAL);
}
});
disclaimerDialog.show();
}
}
};
mContext.registerReceiver(mBCastDownloadConfiguration,
new IntentFilter(GAPPS_ACTION_DOWNLOAD_CONFIGURATION_FILE));
mBCastInstallDownloadCancel = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateWidgetState(GAPPS_STATES_INITIAL);
}
};
mContext.registerReceiver(mBCastInstallDownloadCancel,
new IntentFilter(GOOGLE_APPS_INSTALL_DOWNLOAD_CANCEL));
mBCastGoPermissions = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String filename = mContext.getResources().getString(
R.string.gapps_installer_filename);
pushFileToRecovery(filename);
}
};
mContext.registerReceiver(mBCastGoPermissions, new IntentFilter(
GAPPS_ACTION_GO_PERMISSIONS));
mBCastGappsInstallReboot = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// String filename = mContext.getResources().getString(
// R.string.gapps_installer_filename);
//
// pushFileToRecovery(filename);
// alter State
updateWidgetState(GAPPS_INSTALLED_STATE);
// reboot
rebootToRecovery();
}
};
mContext.registerReceiver(mBCastGappsInstallReboot, new IntentFilter(
GOOGLE_APPS_INSTALL_REBOOT));
}
public void pushFileToRecovery(String fileName) {
if (RootTools.isAccessGiven()) {
// set the command for the recovery
Process p;
try {
p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
os.writeBytes("rm -f /cache/recovery/command\n");
os.writeBytes("rm -f /cache/recovery/extendedcommand\n");
os.writeBytes("echo '--wipe_cache' >> /cache/recovery/command\n");
os.writeBytes("echo '--update_package=/" + RECOVERY_PATH
+ fileName + "' >> /cache/recovery/command\n");
os.writeBytes("sync\n");
os.writeBytes("exit\n");
os.flush();
p.waitFor();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
updateWidgetState(GAPPS_REBOOT_STATE);
}
public void rebootToRecovery() {
if (RootTools.isAccessGiven()) {
updateWidgetState(GAPPS_INSTALLED_STATE);
// reboot
try {
((PowerManager) mContext
.getSystemService(Context.POWER_SERVICE))
.reboot("recovery");
} catch (Throwable t) {
Log.e(TAG, "Could not access files", t);
}
} else {
Resources resources = mContext.getResources();
AlertDialog permissionsDialog = new AlertDialog.Builder(mContext)
.create();
permissionsDialog.setTitle(resources
.getText(R.string.google_apps_denied_permissions_title));
// Setting Dialog Message
permissionsDialog
.setMessage(resources
.getText(R.string.google_apps_denied_permissions_description));
permissionsDialog.setButton(AlertDialog.BUTTON_POSITIVE,
resources.getString(android.R.string.ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
forceCleanConfigurationFile();
// forceCleanUnzipDirectory();
updateWidgetState(GAPPS_STATES_INITIAL);
}
});
permissionsDialog.show();
}
}
private void clearBroadcastReceivers() {
mContext.unregisterReceiver(mBCastDisclaimer);
mContext.unregisterReceiver(mBCastDownloadConfiguration);
mContext.unregisterReceiver(mBCastGoPermissions);
mContext.unregisterReceiver(mBCastGappsInstallReboot);
mContext.unregisterReceiver(mBCastInstallDownloadCancel);
}
private void updateGoogleAppsIntallerWidgets() {
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(mContext);
int[] appWidgetIds = appWidgetManager
.getAppWidgetIds(new ComponentName(mContext,
GoogleAppsInstallerWidget.class));
if (appWidgetIds.length > 0) {
new GoogleAppsInstallerWidget().onUpdate(mContext,
appWidgetManager, appWidgetIds);
}
}
private Request createDownloadRequest(String url, String fileName) {
Request request = new Request(Uri.parse(url));
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).mkdirs();
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS, fileName);
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
request.setAllowedOverRoaming(false);
String download = mContext.getResources().getString(
R.string.google_apps_download_title);
request.setTitle(download);
return request;
}
private void startDownloadProgressUpdateThread(final long download_id) {
new Thread(new Runnable() {
@Override
public void run() {
boolean downloading = true;
while (downloading) {
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(download_id);
Cursor cursor = mDownloadManager.query(q);
if (cursor != null) {
cursor.moveToFirst();
int bytes_downloaded = cursor.getInt(cursor
.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
int bytes_total = cursor.getInt(cursor
.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
if (cursor.getInt(cursor
.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false;
bytes_downloaded = 0;
bytes_total = 0;
}
SharedPreferences.Editor prefEdit = mSharedPrefs.edit();
prefEdit.putInt(
GappsInstallerHelper.GOOGLE_APPS_INSTALLER_PROGRESS,
bytes_downloaded);
prefEdit.putInt(
GappsInstallerHelper.GOOGLE_APPS_INSTALLER_PROGRESS_MAX,
bytes_total);
prefEdit.commit();
updateGoogleAppsIntallerWidgets();
cursor.close();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
boolean unzipped = false;
private void installAppsToLocations() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
updateInstallerState(GAPPS_STATE_INSTALLATION);
runCommandsAsRoot(GappsInstallationAssets.install_files);
} catch (Throwable t) {
Log.e(TAG, "Error while installing the files", t);
}
}
});
thread.start();
}
private void runCommandsAsRoot(String[] files) throws IOException,
TimeoutException, RootDeniedException {
String downloadPath = DOWNLOAD_PATH + ZIP_CONTENT_PATH;
int maxFiles = files.length;
int currentCount = 0;
Shell.runRootCommand(new CommandCapture(0,
GappsInstallationAssets.MOUNT_SYSTEM_RW));
for (String filePath : files) {
Log.d(this.getClass().getSimpleName(), "[INST]installing file :"
+ downloadPath + filePath + " to " + filePath);
Shell.runRootCommand(new CommandCapture(0, "cat " + downloadPath
+ filePath + " > /" + filePath));
currentCount++;
// update progress bar
Editor editor = mSharedPrefs.edit();
editor.putInt(GappsInstallerHelper.GOOGLE_APPS_INSTALLER_PROGRESS,
currentCount);
editor.putInt(
GappsInstallerHelper.GOOGLE_APPS_INSTALLER_PROGRESS_MAX,
maxFiles);
editor.commit();
updateGoogleAppsIntallerWidgets();
}
// update the status of the files
for (String filePath : files) {
Log.d(this.getClass().getSimpleName(),
"[CHMOD]changing permissions for file :" + filePath);
Shell.runRootCommand(new CommandCapture(0, "chmod 644 /" + filePath));
}
Shell.runRootCommand(new CommandCapture(0,
GappsInstallationAssets.MOUNT_SYSTEM_RO));
}
private void updateInstallerState(int state) {
// alter State
SharedPreferences.Editor prefEdit = mSharedPrefs.edit();
prefEdit.putInt(GOOGLE_APPS_INSTALLER_STATE, state);
prefEdit.commit();
}
private int getInstallerState() {
return mSharedPrefs.getInt(GOOGLE_APPS_INSTALLER_STATE,
GAPPS_STATES_INITIAL);
}
private void updateWidgetState(int state) {
updateInstallerState(state);
updateGoogleAppsIntallerWidgets();
}
public static boolean checkMD5(String md5, File updateFile) {
if (!updateFile.exists()) {
return false;
}
if (md5 == null || md5.equals("") || updateFile == null) {
Log.e(TAG, "MD5 String NULL or UpdateFile NULL");
return false;
}
String calculatedDigest = calculateMD5(updateFile);
if (calculatedDigest == null) {
Log.e(TAG, "calculatedDigest NULL");
return false;
}
Log.i(TAG, "Calculated digest: " + calculatedDigest);
Log.i(TAG, "Provided digest: " + md5);
return calculatedDigest.equalsIgnoreCase(md5);
}
public static String calculateMD5(File updateFile) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Exception while getting Digest", e);
return null;
}
InputStream is;
try {
is = new FileInputStream(updateFile);
} catch (FileNotFoundException e) {
Log.e(TAG, "Exception while getting FileInputStream", e);
return null;
}
byte[] buffer = new byte[8192];
int read;
try {
while ((read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1, md5sum);
String output = bigInt.toString(16);
// Fill to 32 chars
output = String.format("%32s", output).replace(' ', '0');
return output;
} catch (IOException e) {
throw new RuntimeException("Unable to process file for MD5", e);
} finally {
try {
is.close();
} catch (IOException e) {
Log.e(TAG, "Exception on closing MD5 input stream", e);
}
}
}
private class DownloadBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
DownloadManager.Query query = new DownloadManager.Query();
long downloadID = 0;
switch (getCurrentState()) {
case GAPPS_STATES_DOWNLOAD_CONFIGURATION_FILE:
downloadID = mConfigFileDownloadId;
break;
default:
downloadID = mSharedPrefs.getLong(GOOGLE_APPS_DOWNLOAD_ID, 0);
}
Cursor cursor = mDownloadManager.query(query);
query.setFilterById(downloadID);
if (cursor.moveToFirst()) {
int columnIndex = cursor
.getColumnIndex(DownloadManager.COLUMN_STATUS);
int status = cursor.getInt(columnIndex);
int columnReason = cursor
.getColumnIndex(DownloadManager.COLUMN_REASON);
int reason = cursor.getInt(columnReason);
if (status == DownloadManager.STATUS_SUCCESSFUL) {
// file to where the download happened
String filePath = mDownloadManager.getUriForDownloadedFile(
downloadID).getPath();
// Retrieve the saved download id
if (downloadID == mConfigFileDownloadId) {
String targetPath = DOWNLOAD_PATH + ZIP_CONTENT_PATH;
String cfgFilename = mContext.getResources().getString(R.string.gapps_installer_config_file);
String fileCfgExt = mContext.getResources().getString(R.string.gapps_installer_cfg);
String cfgFile = targetPath + cfgFilename + fileCfgExt;
if(!checkFileSignature(filePath, targetPath)){
Toast.makeText(mContext,
R.string.google_apps_download_error,
Toast.LENGTH_LONG).show();
updateWidgetState(GAPPS_STATES_INITIAL);
return;
}
// read the gapps url
String[] downloadData = getGappsUrlFromConfigFile(cfgFile);
if (downloadData == null) {
Toast.makeText(mContext,
R.string.google_apps_download_error,
Toast.LENGTH_LONG).show();
updateWidgetState(GAPPS_STATES_INITIAL);
return;
}
// read the md5
mMD5hash = downloadData[1];
String filename = mContext.getResources().getString(
R.string.gapps_installer_filename);
if (hasAlreadyDownloadedZipFile(mMD5hash, filename)) {
updateWidgetState(GAPPS_STATES_PERMISSION_CHECK);
// updateWidgetState(GAPPS_REBOOT_STATE);
} else {
Log.d(TAG, "GAPPS> file does not match");
forceCleanGappsZipFile();
// enqueue of gapps request
Request request = createDownloadRequest(
downloadData[0], filename);
mGappsFileDownloadId = mDownloadManager
.enqueue(request);
SharedPreferences.Editor prefEdit = mSharedPrefs
.edit();
// Save the download id
prefEdit.putLong(GOOGLE_APPS_DOWNLOAD_ID,
mGappsFileDownloadId);
startDownloadProgressUpdateThread(mGappsFileDownloadId);
prefEdit.putInt(
GappsInstallerHelper.GOOGLE_APPS_INSTALLER_PROGRESS,
0);
prefEdit.putInt(
GappsInstallerHelper.GOOGLE_APPS_INSTALLER_PROGRESS_MAX,
0);
prefEdit.commit();
// alter Widget State
updateGoogleAppsIntallerWidgets();
updateWidgetState(GAPPS_STATES_DOWNLOAD_GOOGLE_APPS_FILE);
}
} else {
updateWidgetState(GAPPS_STATES_PERMISSION_CHECK);
// updateWidgetState(GAPPS_REBOOT_STATE);
}
} else if (status == DownloadManager.STATUS_FAILED) {
Toast.makeText(mContext,
"FAILED!\n" + "reason of " + reason,
Toast.LENGTH_LONG).show();
forceCleanConfigurationFile();
forceCleanUnzipDirectory();
updateWidgetState(GAPPS_STATES_INITIAL);
} else if (status == DownloadManager.STATUS_PAUSED) {
Toast.makeText(mContext,
"PAUSED!\n" + "reason of " + reason,
Toast.LENGTH_LONG).show();
} else if (status == DownloadManager.STATUS_PENDING) {
Toast.makeText(mContext, "PENDING!", Toast.LENGTH_LONG)
.show();
} else if (status == DownloadManager.STATUS_RUNNING) {
Toast.makeText(mContext, "RUNNING!", Toast.LENGTH_LONG)
.show();
}
}
}
}
private boolean checkFileSignature(String filePath, String targetPath){
boolean valid = false;
unzip(filePath, targetPath);
try {
String cfgFilename = mContext.getResources().getString(R.string.gapps_installer_config_file);
String fileCfgExt = mContext.getResources().getString(R.string.gapps_installer_cfg);
String fileSigExt = mContext.getResources().getString(R.string.gapps_installer_sig);
PublicKey pubKey = RSAUtils.readPublicKeyFromPemFormat(mContext, R.raw.public_key);
byte[] sign = RSAUtils.readSignature(targetPath + cfgFilename + fileSigExt);
valid = RSAUtils.verifySignature(targetPath + cfgFilename + fileCfgExt, RSAUtils.SIGNATURE_ALGORITHM, sign, pubKey);
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return valid;
}
public void unzip(String filePath, String targetPath) {
new File(targetPath).mkdirs();
try {
FileInputStream fin = new FileInputStream(filePath);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.d(TAG, "Unzipping " + ze.getName());
if (ze.isDirectory()) {
_dirChecker(ze.getName(), targetPath);
} else {
FileOutputStream fout = new FileOutputStream(targetPath + ze.getName());
byte buffer[] = new byte[2048];
int count = 0;
while ((count = zin.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
fin.close();
} catch (Exception e) {
Log.e("Decompress", "unzip", e);
}
}
private void _dirChecker(String dir, String location) {
File f = new File(location + dir);
if (!f.isDirectory()) {
f.mkdirs();
}
}
}