From d5f4c1bcec9cf7503c34bef979662332f44b60af Mon Sep 17 00:00:00 2001 From: Gabriele M Date: Thu, 5 Apr 2018 21:54:44 +0200 Subject: [PATCH] Detect update failures This allows to delete the update only if the installation succeeded and to notify the user in case the installation failed. Since we now need the file in case of failure, create a copy for uncrypt even if the user chose to delete installed updates. Change-Id: I80b0f499663bbf50bcbca5f643c01ffdb4cd3957 --- res/values/strings.xml | 2 + .../lineageos/updater/UpdaterReceiver.java | 60 +++++++++++++++++++ .../updater/controller/UpdateInstaller.java | 25 +++++--- src/org/lineageos/updater/misc/Constants.java | 5 ++ src/org/lineageos/updater/misc/Utils.java | 17 +++++- 5 files changed, 99 insertions(+), 10 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 3efd7987..bb6f50d7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -41,6 +41,7 @@ Download error Download completed Starting download + Update failed New updates @@ -139,4 +140,5 @@ Export completion New updates Ongoing downloads + Update failed diff --git a/src/org/lineageos/updater/UpdaterReceiver.java b/src/org/lineageos/updater/UpdaterReceiver.java index ca099144..6ef44056 100644 --- a/src/org/lineageos/updater/UpdaterReceiver.java +++ b/src/org/lineageos/updater/UpdaterReceiver.java @@ -15,20 +15,75 @@ */ package org.lineageos.updater; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.PowerManager; +import android.os.SystemProperties; +import android.support.v4.app.NotificationCompat; import android.support.v7.preference.PreferenceManager; +import org.lineageos.updater.misc.BuildInfoUtils; import org.lineageos.updater.misc.Constants; +import org.lineageos.updater.misc.StringGenerator; + +import java.text.DateFormat; public class UpdaterReceiver extends BroadcastReceiver { public static final String ACTION_INSTALL_REBOOT = "org.lineageos.updater.action.INSTALL_REBOOT"; + private static final String INSTALL_ERROR_NOTIFICATION_CHANNEL = + "install_error_notification_channel"; + + private static boolean shouldShowUpdateFailedNotification(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + + // We can't easily detect failed re-installations + if (preferences.getBoolean(Constants.PREF_INSTALL_AGAIN, false) || + preferences.getBoolean(Constants.PREF_INSTALL_NOTIFIED, false)) { + return false; + } + + long buildTimestamp = SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0); + long lastBuildTimestamp = preferences.getLong(Constants.PREF_INSTALL_OLD_TIMESTAMP, -1); + return buildTimestamp == lastBuildTimestamp; + } + + private static void showUpdateFailedNotification(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + String buildDate = StringGenerator.getDateLocalizedUTC(context, + DateFormat.MEDIUM, preferences.getLong(Constants.PREF_INSTALL_NEW_TIMESTAMP, 0)); + String buildInfo = context.getString(R.string.list_build_version_date, + BuildInfoUtils.getBuildVersion(), buildDate); + + Intent notificationIntent = new Intent(context, UpdatesActivity.class); + PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationChannel notificationChannel = new NotificationChannel( + INSTALL_ERROR_NOTIFICATION_CHANNEL, + context.getString(R.string.update_failed_channel_title), + NotificationManager.IMPORTANCE_LOW); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + INSTALL_ERROR_NOTIFICATION_CHANNEL) + .setContentIntent(intent) + .setSmallIcon(R.drawable.ic_system_update) + .setContentTitle(context.getString(R.string.update_failed_notification)) + .setStyle(new NotificationCompat.BigTextStyle().bigText(buildInfo)) + .setContentText(buildInfo); + + NotificationManager nm = (NotificationManager) context.getSystemService( + Context.NOTIFICATION_SERVICE); + nm.createNotificationChannel(notificationChannel); + nm.notify(0, builder.build()); + } + @Override public void onReceive(Context context, Intent intent) { if (ACTION_INSTALL_REBOOT.equals(intent.getAction())) { @@ -37,6 +92,11 @@ public class UpdaterReceiver extends BroadcastReceiver { } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); pref.edit().remove(Constants.PREF_NEEDS_REBOOT).apply(); + + if (shouldShowUpdateFailedNotification(context)) { + pref.edit().putBoolean(Constants.PREF_INSTALL_NOTIFIED, true).apply(); + showUpdateFailedNotification(context); + } } } } diff --git a/src/org/lineageos/updater/controller/UpdateInstaller.java b/src/org/lineageos/updater/controller/UpdateInstaller.java index aaae06b9..ccac1c92 100644 --- a/src/org/lineageos/updater/controller/UpdateInstaller.java +++ b/src/org/lineageos/updater/controller/UpdateInstaller.java @@ -16,7 +16,9 @@ package org.lineageos.updater.controller; import android.content.Context; +import android.content.SharedPreferences; import android.os.SystemClock; +import android.os.SystemProperties; import android.support.v7.preference.PreferenceManager; import android.util.Log; @@ -55,15 +57,20 @@ class UpdateInstaller { void install(String downloadId) { UpdateInfo update = mUpdaterController.getUpdate(downloadId); - boolean deleteUpdate = PreferenceManager.getDefaultSharedPreferences(mContext) - .getBoolean(Constants.PREF_AUTO_DELETE_UPDATES, false); - if (deleteUpdate) { - // Renaming the file is enough to have it deleted automatically - File uncrytpFile = new File( - update.getFile().getAbsolutePath() + Constants.UNCRYPT_FILE_EXT); - update.getFile().renameTo(uncrytpFile); - installPackage(uncrytpFile, downloadId); - } else if (Utils.isEncrypted(mContext, update.getFile())) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + long buildTimestamp = SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0); + long lastBuildTimestamp = preferences.getLong(Constants.PREF_INSTALL_OLD_TIMESTAMP, + buildTimestamp); + boolean isReinstalling = buildTimestamp == lastBuildTimestamp; + preferences.edit() + .putLong(Constants.PREF_INSTALL_OLD_TIMESTAMP, buildTimestamp) + .putLong(Constants.PREF_INSTALL_NEW_TIMESTAMP, update.getTimestamp()) + .putString(Constants.PREF_INSTALL_PACKAGE_PATH, update.getFile().getAbsolutePath()) + .putBoolean(Constants.PREF_INSTALL_AGAIN, isReinstalling) + .putBoolean(Constants.PREF_INSTALL_NOTIFIED, false) + .apply(); + + if (Utils.isEncrypted(mContext, update.getFile())) { // uncrypt rewrites the file so that it can be read without mounting // the filesystem, so create a copy of it. prepareForUncryptAndInstall(update); diff --git a/src/org/lineageos/updater/misc/Constants.java b/src/org/lineageos/updater/misc/Constants.java index b5764ef8..49106d4a 100644 --- a/src/org/lineageos/updater/misc/Constants.java +++ b/src/org/lineageos/updater/misc/Constants.java @@ -39,4 +39,9 @@ public final class Constants { public static final String PROP_RELEASE_TYPE = "ro.lineage.releasetype"; public static final String PROP_UPDATER_URI = "lineage.updater.uri"; + public static final String PREF_INSTALL_OLD_TIMESTAMP = "install_old_timestamp"; + public static final String PREF_INSTALL_NEW_TIMESTAMP = "install_new_timestamp"; + public static final String PREF_INSTALL_PACKAGE_PATH = "install_package_path"; + public static final String PREF_INSTALL_AGAIN = "install_again"; + public static final String PREF_INSTALL_NOTIFIED = "install_notified"; } diff --git a/src/org/lineageos/updater/misc/Utils.java b/src/org/lineageos/updater/misc/Utils.java index 255b750e..67895daf 100644 --- a/src/org/lineageos/updater/misc/Utils.java +++ b/src/org/lineageos/updater/misc/Utils.java @@ -264,11 +264,26 @@ public class Utils { */ public static void cleanupDownloadsDir(Context context) { File downloadPath = getDownloadPath(context); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); removeUncryptFiles(downloadPath); + long buildTimestamp = SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0); + long prevTimestamp = preferences.getLong(Constants.PREF_INSTALL_OLD_TIMESTAMP, 0); + String lastUpdatePath = preferences.getString(Constants.PREF_INSTALL_PACKAGE_PATH, null); + boolean reinstalling = preferences.getBoolean(Constants.PREF_INSTALL_AGAIN, false); + boolean deleteUpdates = preferences.getBoolean(Constants.PREF_AUTO_DELETE_UPDATES, false); + if ((buildTimestamp != prevTimestamp || reinstalling) && deleteUpdates && + lastUpdatePath != null) { + File lastUpdate = new File(lastUpdatePath); + if (lastUpdate.exists()) { + lastUpdate.delete(); + // Remove the pref not to delete the file if re-downloaded + preferences.edit().remove(Constants.PREF_INSTALL_PACKAGE_PATH).apply(); + } + } + final String DOWNLOADS_CLEANUP_DONE = "cleanup_done"; - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); if (preferences.getBoolean(DOWNLOADS_CLEANUP_DONE, false)) { return; }