Loading packages/DynamicSystemInstallationService/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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> Loading packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +15 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -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. } Loading Loading @@ -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; Loading @@ -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--) { Loading packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +72 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -115,6 +145,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog InstallationAsyncTask( String url, String dsuSlot, String publicKey, long systemSize, long userdataSize, Context context, Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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 { Loading Loading @@ -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(); Loading @@ -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); Loading @@ -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(); Loading @@ -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); Loading @@ -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)); Loading Loading @@ -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() { Loading Loading
packages/DynamicSystemInstallationService/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +15 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -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. } Loading Loading @@ -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; Loading @@ -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--) { Loading
packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +72 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -115,6 +145,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog InstallationAsyncTask( String url, String dsuSlot, String publicKey, long systemSize, long userdataSize, Context context, Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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 { Loading Loading @@ -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(); Loading @@ -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); Loading @@ -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(); Loading @@ -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); Loading @@ -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)); Loading Loading @@ -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() { Loading