Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d5f4c1bc authored by Gabriele M's avatar Gabriele M
Browse files

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
parent af9b4adb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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>
+60 −0
Original line number Diff line number Diff line
@@ -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);
            }
        }
    }
}
+16 −9
Original line number Diff line number Diff line
@@ -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);
+5 −0
Original line number Diff line number Diff line
@@ -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";
}
+16 −1
Original line number Diff line number Diff line
@@ -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;
        }