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

Commit 55e15104 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Check DSU public key with key revocation list" am: 3bdf29c1

Change-Id: I84f160a92e456e3bb70833ef8a484b73628523f8
parents 6b281c93 3bdf29c1
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@
    <string name="notification_install_inprogress">Install in progress</string>
    <!-- Displayed on notification: Dynamic System installation failed [CHAR LIMIT=128] -->
    <string name="notification_install_failed">Install failed</string>
    <!-- Displayed on notification: Image validation failed [CHAR LIMIT=128] -->
    <string name="notification_image_validation_failed">Image validation failed. Abort installation.</string>
    <!-- Displayed on notification: We are running in Dynamic System [CHAR LIMIT=128] -->
    <string name="notification_dynsystem_in_use">Currently running a dynamic system. Restart to use the original Android version.</string>

+15 −4
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ public class DynamicSystemInstallationService extends Service
    static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
    static final String KEY_DSU_SLOT = "KEY_DSU_SLOT";
    static final String DEFAULT_DSU_SLOT = "dsu";
    static final String KEY_PUBKEY = "KEY_PUBKEY";

    /*
     * Intent actions
@@ -267,6 +268,7 @@ public class DynamicSystemInstallationService extends Service
        long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
        mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
        String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
        String publicKey = intent.getStringExtra(KEY_PUBKEY);

        if (TextUtils.isEmpty(dsuSlot)) {
            dsuSlot = DEFAULT_DSU_SLOT;
@@ -274,7 +276,7 @@ public class DynamicSystemInstallationService extends Service
        // TODO: better constructor or builder
        mInstallTask =
                new InstallationAsyncTask(
                        url, dsuSlot, systemSize, userdataSize, this, mDynSystem, this);
                        url, dsuSlot, publicKey, systemSize, userdataSize, this, mDynSystem, this);

        mInstallTask.execute();

@@ -408,6 +410,10 @@ public class DynamicSystemInstallationService extends Service
    }

    private Notification buildNotification(int status, int cause) {
        return buildNotification(status, cause, null);
    }

    private Notification buildNotification(int status, int cause, Throwable detail) {
        Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
                .setProgress(0, 0, false);
@@ -463,7 +469,12 @@ public class DynamicSystemInstallationService extends Service

            case STATUS_NOT_STARTED:
                if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
                    if (detail instanceof InstallationAsyncTask.ImageValidationException) {
                        builder.setContentText(
                                getString(R.string.notification_image_validation_failed));
                    } else {
                        builder.setContentText(getString(R.string.notification_install_failed));
                    }
                } else {
                    // no need to notify the user if the task is not started, or cancelled.
                }
@@ -525,7 +536,7 @@ public class DynamicSystemInstallationService extends Service
                break;
        }

        Log.d(TAG, "status=" + statusString + ", cause=" + causeString);
        Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);

        boolean notifyOnNotificationBar = true;

@@ -538,7 +549,7 @@ public class DynamicSystemInstallationService extends Service
        }

        if (notifyOnNotificationBar) {
            mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
            mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
        }

        for (int i = mClients.size() - 1; i >= 0; i--) {
+72 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.dynsystem;

import android.content.Context;
import android.gsi.AvbPublicKey;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.MemoryFile;
@@ -51,18 +52,46 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
    private static final List<String> UNSUPPORTED_PARTITIONS =
            Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");

    private class UnsupportedUrlException extends RuntimeException {
    private class UnsupportedUrlException extends Exception {
        private UnsupportedUrlException(String message) {
            super(message);
        }
    }

    private class UnsupportedFormatException extends RuntimeException {
    private class UnsupportedFormatException extends Exception {
        private UnsupportedFormatException(String message) {
            super(message);
        }
    }

    static class ImageValidationException extends Exception {
        ImageValidationException(String message) {
            super(message);
        }

        ImageValidationException(Throwable cause) {
            super(cause);
        }
    }

    static class RevocationListFetchException extends ImageValidationException {
        RevocationListFetchException(Throwable cause) {
            super(cause);
        }
    }

    static class KeyRevokedException extends ImageValidationException {
        KeyRevokedException(String message) {
            super(message);
        }
    }

    static class PublicKeyException extends ImageValidationException {
        PublicKeyException(String message) {
            super(message);
        }
    }

    /** UNSET means the installation is not completed */
    static final int RESULT_UNSET = 0;
    static final int RESULT_OK = 1;
@@ -97,6 +126,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog

    private final String mUrl;
    private final String mDsuSlot;
    private final String mPublicKey;
    private final long mSystemSize;
    private final long mUserdataSize;
    private final Context mContext;
@@ -115,6 +145,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
    InstallationAsyncTask(
            String url,
            String dsuSlot,
            String publicKey,
            long systemSize,
            long userdataSize,
            Context context,
@@ -122,6 +153,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
            ProgressListener listener) {
        mUrl = url;
        mDsuSlot = dsuSlot;
        mPublicKey = publicKey;
        mSystemSize = systemSize;
        mUserdataSize = userdataSize;
        mContext = context;
@@ -157,8 +189,6 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
                return null;
            }

            // TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk)

            mDynSystem.finishInstallation();
        } catch (Exception e) {
            Log.e(TAG, e.toString(), e);
@@ -247,13 +277,16 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
            String listUrl = mContext.getString(R.string.key_revocation_list_url);
            mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
        } catch (IOException | JSONException e) {
            Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List");
            mKeyRevocationList = new KeyRevocationList();
            keyRevocationThrowOrWarning(e);
            imageValidationThrowOrWarning(new RevocationListFetchException(e));
        }
        if (mKeyRevocationList.isRevoked(mPublicKey)) {
            imageValidationThrowOrWarning(new KeyRevokedException(mPublicKey));
        }
    }

    private void keyRevocationThrowOrWarning(Exception e) throws Exception {
    private void imageValidationThrowOrWarning(ImageValidationException e)
            throws ImageValidationException {
        if (mIsNetworkUrl) {
            throw e;
        } else {
@@ -294,7 +327,8 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
        }
    }

    private void installImages() throws IOException, InterruptedException {
    private void installImages()
            throws IOException, InterruptedException, ImageValidationException {
        if (mStream != null) {
            if (mIsZip) {
                installStreamingZipUpdate();
@@ -306,12 +340,14 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
        }
    }

    private void installStreamingGzUpdate() throws IOException, InterruptedException {
    private void installStreamingGzUpdate()
            throws IOException, InterruptedException, ImageValidationException {
        Log.d(TAG, "To install a streaming GZ update");
        installImage("system", mSystemSize, new GZIPInputStream(mStream), 1);
    }

    private void installStreamingZipUpdate() throws IOException, InterruptedException {
    private void installStreamingZipUpdate()
            throws IOException, InterruptedException, ImageValidationException {
        Log.d(TAG, "To install a streaming ZIP update");

        ZipInputStream zis = new ZipInputStream(mStream);
@@ -330,7 +366,8 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
        }
    }

    private void installLocalZipUpdate() throws IOException, InterruptedException {
    private void installLocalZipUpdate()
            throws IOException, InterruptedException, ImageValidationException {
        Log.d(TAG, "To install a local ZIP update");

        Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -349,8 +386,9 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
        }
    }

    private boolean installImageFromAnEntry(ZipEntry entry, InputStream is,
            int numInstalledPartitions) throws IOException, InterruptedException {
    private boolean installImageFromAnEntry(
            ZipEntry entry, InputStream is, int numInstalledPartitions)
            throws IOException, InterruptedException, ImageValidationException {
        String name = entry.getName();

        Log.d(TAG, "ZipEntry: " + name);
@@ -373,8 +411,9 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
        return true;
    }

    private void installImage(String partitionName, long uncompressedSize, InputStream is,
            int numInstalledPartitions) throws IOException, InterruptedException {
    private void installImage(
            String partitionName, long uncompressedSize, InputStream is, int numInstalledPartitions)
            throws IOException, InterruptedException, ImageValidationException {

        SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));

@@ -445,6 +484,24 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
                publishProgress(progress);
            }
        }

        AvbPublicKey avbPublicKey = new AvbPublicKey();
        if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) {
            imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed"));
        } else {
            String publicKey = toHexString(avbPublicKey.sha1);
            if (mKeyRevocationList.isRevoked(publicKey)) {
                imageValidationThrowOrWarning(new KeyRevokedException(publicKey));
            }
        }
    }

    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    private void close() {