diff --git a/res/values/strings.xml b/res/values/strings.xml index 94e844f0..0b6ae93f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -48,6 +48,8 @@ Update installed Finalizing package installation Preparing for first boot + Preliminary update preparation + Could not prepare update Reboot diff --git a/src/org/lineageos/updater/controller/UpdaterService.java b/src/org/lineageos/updater/controller/UpdaterService.java index 57e23712..c3600329 100644 --- a/src/org/lineageos/updater/controller/UpdaterService.java +++ b/src/org/lineageos/updater/controller/UpdaterService.java @@ -34,11 +34,14 @@ import org.lineageos.updater.R; import org.lineageos.updater.UpdaterReceiver; import org.lineageos.updater.UpdatesActivity; import org.lineageos.updater.misc.BuildInfoUtils; +import org.lineageos.updater.misc.Constants; +import org.lineageos.updater.misc.FileUtils; import org.lineageos.updater.misc.StringGenerator; import org.lineageos.updater.misc.Utils; import org.lineageos.updater.model.UpdateInfo; import org.lineageos.updater.model.UpdateStatus; +import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.NumberFormat; @@ -169,7 +172,22 @@ public class UpdaterService extends Service { if (Utils.isABUpdate(update.getFile())) { ABUpdateInstaller.start(mUpdaterController, downloadId); } else { - android.os.RecoverySystem.installPackage(this, update.getFile()); + if (update.getFile().getAbsolutePath().startsWith("/data/") && + Utils.isDeviceEncrypted(this)) { + // uncrypt rewrites the file so that it can be read without mounting + // the filesystem, so create a copy of it. + File uncrytpFile = new File( + update.getFile().getAbsolutePath() + Constants.UNCRYPT_FILE_EXT); + FileUtils.prepareForUncrypt(this, update.getFile(), uncrytpFile, + new Runnable() { + @Override + public void run() { + installPackage(uncrytpFile); + } + }); + } else { + installPackage(update.getFile()); + } } } catch (IOException e) { Log.e(TAG, "Could not install update", e); @@ -180,6 +198,15 @@ public class UpdaterService extends Service { return START_NOT_STICKY; } + private void installPackage(File update) { + try { + android.os.RecoverySystem.installPackage(this, update); + } catch (IOException e) { + // TODO: show error message + Log.e(TAG, "Could not install update", e); + } + } + public Controller getUpdaterController() { return mUpdaterController; } diff --git a/src/org/lineageos/updater/misc/Constants.java b/src/org/lineageos/updater/misc/Constants.java index 570fd4be..5b27bb27 100644 --- a/src/org/lineageos/updater/misc/Constants.java +++ b/src/org/lineageos/updater/misc/Constants.java @@ -26,6 +26,8 @@ public final class Constants { public static final String PREF_LAST_UPDATE_CHECK = "last_update_check"; public static final String PREF_AUTO_UPDATES_CHECK = "auto_updates_check"; + public static final String UNCRYPT_FILE_EXT = ".uncrypt"; + public static final String PROP_BUILD_DATE = "ro.build.date.utc"; public static final String PROP_BUILD_VERSION = "ro.cm.build.version"; public static final String PROP_BUILD_VERSION_INCREMENTAL = "ro.build.version.incremental"; diff --git a/src/org/lineageos/updater/misc/FileUtils.java b/src/org/lineageos/updater/misc/FileUtils.java index c6f7f444..0cfbf933 100644 --- a/src/org/lineageos/updater/misc/FileUtils.java +++ b/src/org/lineageos/updater/misc/FileUtils.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.os.AsyncTask; import android.support.v7.app.NotificationCompat; import android.util.Log; +import android.view.WindowManager; import org.lineageos.updater.R; @@ -177,4 +178,79 @@ public class FileUtils { } }.execute(); } + + public static void prepareForUncrypt(Context context, File updateFile, File uncryptFile, + Runnable callback) { + + final int NOTIFICATION_ID = 12; + + new AsyncTask() { + + private ProgressDialog mProgressDialog; + private boolean mCancelled; + private ProgressCallBack mProgressCallBack; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + Log.d(TAG, "Preparing update"); + mProgressDialog = new ProgressDialog(context); + mProgressDialog.setTitle(R.string.app_name); + mProgressDialog.setMessage(context.getString(R.string.dialog_prepare_zip_message)); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + mProgressDialog.setCancelable(true); + mProgressDialog.setProgressNumberFormat(null); + mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + cancel(true); + } + }); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressCallBack = new ProgressCallBack() { + @Override + public void update(int progress) { + mProgressDialog.setProgress(progress); + } + }; + mProgressDialog.show(); + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + copyFile(updateFile, uncryptFile, mProgressCallBack); + } catch (IOException e) { + Log.e(TAG, "Error while copying the file", e); + } + return !mCancelled; + } + + @Override + protected void onCancelled() { + mCancelled = true; + uncryptFile.delete(); + } + + @Override + protected void onPostExecute(Boolean success) { + if (!success || mCancelled) { + Log.e(TAG, "Could not prepare the update, cancelled=" + mCancelled); + uncryptFile.delete(); + NotificationManager nm = (NotificationManager) context.getSystemService( + Context.NOTIFICATION_SERVICE); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + builder.setSmallIcon(R.drawable.ic_system_update); + builder.setContentTitle( + context.getString(R.string.notification_prepare_zip_error_title)); + final String notificationTag = updateFile.getAbsolutePath(); + nm.notify(notificationTag, NOTIFICATION_ID, builder.build()); + } else { + callback.run(); + } + mProgressDialog.dismiss(); + } + }.execute(); + } } diff --git a/src/org/lineageos/updater/misc/Utils.java b/src/org/lineageos/updater/misc/Utils.java index 74b55ce5..0426f110 100644 --- a/src/org/lineageos/updater/misc/Utils.java +++ b/src/org/lineageos/updater/misc/Utils.java @@ -15,6 +15,7 @@ */ package org.lineageos.updater.misc; +import android.app.admin.DevicePolicyManager; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -42,6 +43,7 @@ import org.lineageos.updater.model.UpdateInfo; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; +import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; @@ -222,6 +224,21 @@ public class Utils { throw new IllegalArgumentException("The given entry was not found"); } + public static void removeUncryptFiles(File downloadPath) { + File[] uncryptFiles = downloadPath.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(Constants.UNCRYPT_FILE_EXT); + } + }); + if (uncryptFiles == null) { + return; + } + for (File file : uncryptFiles) { + file.delete(); + } + } + /** * Cleanup the download directory, which is assumed to be a privileged location * the user can't access and that might have stale files. This can happen if @@ -230,14 +247,16 @@ public class Utils { * @param context */ public static void cleanupDownloadsDir(Context context) { - final String DOWNLOADS_CLEANUP_DONE = "cleanup_done"; + File downloadPath = getDownloadPath(context); + removeUncryptFiles(downloadPath); + + final String DOWNLOADS_CLEANUP_DONE = "cleanup_done"; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); if (preferences.getBoolean(DOWNLOADS_CLEANUP_DONE, false)) { return; } - File downloadPath = getDownloadPath(context); Log.d(TAG, "Cleaning " + downloadPath); if (!downloadPath.isDirectory()) { return; @@ -307,4 +326,11 @@ public class Utils { clipboard.setPrimaryClip(clip); Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show(); } + + public static boolean isDeviceEncrypted(Context context) { + DevicePolicyManager dpm = + (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + int status = dpm.getStorageEncryptionStatus(); + return status == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE; + } }