Merge "Check DSU public key with key revocation list" am: 3bdf29c1f6 am: 55e1510484
Change-Id: I775193981379471b43c213b479f1b1a8429c72ab
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
builder.setContentText(getString(R.string.notification_install_failed));
|
||||
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--) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user