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.
This commit is contained in:
Gabriele M
2017-07-21 01:17:19 +02:00
parent 09968f1b25
commit 20ea513684
5 changed files with 136 additions and 3 deletions

View File

@@ -48,6 +48,8 @@
<string name="installing_update_finished">Update installed</string> <string name="installing_update_finished">Update installed</string>
<string name="finalizing_package">Finalizing package installation</string> <string name="finalizing_package">Finalizing package installation</string>
<string name="preparing_ota_first_boot">Preparing for first boot</string> <string name="preparing_ota_first_boot">Preparing for first boot</string>
<string name="dialog_prepare_zip_message">Preliminary update preparation</string>
<string name="notification_prepare_zip_error_title">Could not prepare update</string>
<string name="reboot">Reboot</string> <string name="reboot">Reboot</string>

View File

@@ -34,11 +34,14 @@ import org.lineageos.updater.R;
import org.lineageos.updater.UpdaterReceiver; import org.lineageos.updater.UpdaterReceiver;
import org.lineageos.updater.UpdatesActivity; import org.lineageos.updater.UpdatesActivity;
import org.lineageos.updater.misc.BuildInfoUtils; 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.StringGenerator;
import org.lineageos.updater.misc.Utils; import org.lineageos.updater.misc.Utils;
import org.lineageos.updater.model.UpdateInfo; import org.lineageos.updater.model.UpdateInfo;
import org.lineageos.updater.model.UpdateStatus; import org.lineageos.updater.model.UpdateStatus;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
@@ -169,7 +172,22 @@ public class UpdaterService extends Service {
if (Utils.isABUpdate(update.getFile())) { if (Utils.isABUpdate(update.getFile())) {
ABUpdateInstaller.start(mUpdaterController, downloadId); ABUpdateInstaller.start(mUpdaterController, downloadId);
} else { } 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) { } catch (IOException e) {
Log.e(TAG, "Could not install update", e); Log.e(TAG, "Could not install update", e);
@@ -180,6 +198,15 @@ public class UpdaterService extends Service {
return START_NOT_STICKY; 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() { public Controller getUpdaterController() {
return mUpdaterController; return mUpdaterController;
} }

View File

@@ -26,6 +26,8 @@ public final class Constants {
public static final String PREF_LAST_UPDATE_CHECK = "last_update_check"; 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 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_DATE = "ro.build.date.utc";
public static final String PROP_BUILD_VERSION = "ro.cm.build.version"; public static final String PROP_BUILD_VERSION = "ro.cm.build.version";
public static final String PROP_BUILD_VERSION_INCREMENTAL = "ro.build.version.incremental"; public static final String PROP_BUILD_VERSION_INCREMENTAL = "ro.build.version.incremental";

View File

@@ -22,6 +22,7 @@ import android.content.DialogInterface;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.v7.app.NotificationCompat; import android.support.v7.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.view.WindowManager;
import org.lineageos.updater.R; import org.lineageos.updater.R;
@@ -177,4 +178,79 @@ public class FileUtils {
} }
}.execute(); }.execute();
} }
public static void prepareForUncrypt(Context context, File updateFile, File uncryptFile,
Runnable callback) {
final int NOTIFICATION_ID = 12;
new AsyncTask<Void, String, Boolean>() {
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();
}
} }

View File

@@ -15,6 +15,7 @@
*/ */
package org.lineageos.updater.misc; package org.lineageos.updater.misc;
import android.app.admin.DevicePolicyManager;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
@@ -42,6 +43,7 @@ import org.lineageos.updater.model.UpdateInfo;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
@@ -222,6 +224,21 @@ public class Utils {
throw new IllegalArgumentException("The given entry was not found"); 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 * 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 * the user can't access and that might have stale files. This can happen if
@@ -230,14 +247,16 @@ public class Utils {
* @param context * @param context
*/ */
public static void cleanupDownloadsDir(Context 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); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences.getBoolean(DOWNLOADS_CLEANUP_DONE, false)) { if (preferences.getBoolean(DOWNLOADS_CLEANUP_DONE, false)) {
return; return;
} }
File downloadPath = getDownloadPath(context);
Log.d(TAG, "Cleaning " + downloadPath); Log.d(TAG, "Cleaning " + downloadPath);
if (!downloadPath.isDirectory()) { if (!downloadPath.isDirectory()) {
return; return;
@@ -307,4 +326,11 @@ public class Utils {
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show(); 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;
}
} }