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
This commit is contained in:
Gabriele M
2018-04-05 21:54:44 +02:00
parent af9b4adbe0
commit d5f4c1bcec
5 changed files with 99 additions and 10 deletions

View File

@@ -41,6 +41,7 @@
<string name="download_paused_error_notification">Download error</string>
<string name="download_completed_notification">Download completed</string>
<string name="download_starting_notification">Starting download</string>
<string name="update_failed_notification">Update failed</string>
<string name="new_updates_found_title">New updates</string>
@@ -139,4 +140,5 @@
<string name="export_channel_title">Export completion</string>
<string name="new_updates_channel_title">New updates</string>
<string name="ongoing_channel_title">Ongoing downloads</string>
<string name="update_failed_channel_title">Update failed</string>
</resources>

View File

@@ -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);
}
}
}
}

View File

@@ -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);

View File

@@ -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";
}

View File

@@ -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;
}