From 20ea5136845ae4c108167bb98d5621d76df124eb Mon Sep 17 00:00:00 2001 From: Gabriele M Date: Fri, 21 Jul 2017 01:17:19 +0200 Subject: [PATCH] Create a copy of the zips on encrypted devices On encrypted devices, uncrypt modifies the zip so that it can be read without mounting the filesystem. Instead of installing the zip downloaded, create a copy of it which will be deleted. This will allow to re-install the zip multiple times or export it after installing it. --- res/values/strings.xml | 2 + .../updater/controller/UpdaterService.java | 29 ++++++- src/org/lineageos/updater/misc/Constants.java | 2 + src/org/lineageos/updater/misc/FileUtils.java | 76 +++++++++++++++++++ src/org/lineageos/updater/misc/Utils.java | 30 +++++++- 5 files changed, 136 insertions(+), 3 deletions(-) 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; + } }