DSU service: Weighted progress bar

Weight progress of writable partitions less because they take much less
time to complete compared to readonly partitions. This is best effort
only and done only when we can deduce the total number of partitions
beforehand.
Also publish the weighted progress to events log so external tools can
use this value directly, instead of having to calculate this complex
progress weighting themselves.
Revise the events log field name to better reflect the "unit" the
metrics are in (size -> bytes, partition -> partition_name).
DynamicSystemInstallationService notification progress should use this
weighted progress if available.

Bug: 169036106
Bug: 224909776
Test: Start a DSU installation and observe the logcat and system notification
Change-Id: I81561c1994698387b275cab36ee058dd581f9879
This commit is contained in:
Yi-Yo Chiang
2022-03-25 19:01:31 +08:00
parent b344dfaee9
commit 3a9690c357
3 changed files with 146 additions and 60 deletions

View File

@@ -112,18 +112,20 @@ public class DynamicSystemInstallationService extends Service
private static final int EVENT_DSU_INSTALL_FAILED = 120002;
protected static void logEventProgressUpdate(
String partition,
long installedSize,
long partitionSize,
String partitionName,
long installedBytes,
long totalBytes,
int partitionNumber,
int totalPartitionNumber) {
int totalPartitionNumber,
int totalProgressPercentage) {
EventLog.writeEvent(
EVENT_DSU_PROGRESS_UPDATE,
partition,
installedSize,
partitionSize,
partitionName,
installedBytes,
totalBytes,
partitionNumber,
totalPartitionNumber);
totalPartitionNumber,
totalProgressPercentage);
}
protected static void logEventComplete() {
@@ -231,10 +233,11 @@ public class DynamicSystemInstallationService extends Service
public void onProgressUpdate(InstallationAsyncTask.Progress progress) {
logEventProgressUpdate(
progress.partitionName,
progress.installedSize,
progress.partitionSize,
progress.installedBytes,
progress.totalBytes,
progress.partitionNumber,
progress.totalPartitionNumber);
progress.totalPartitionNumber,
progress.totalProgressPercentage);
mInstallTaskProgress = progress;
postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
@@ -486,20 +489,28 @@ public class DynamicSystemInstallationService extends Service
builder.setContentText(getString(R.string.notification_install_inprogress));
if (mInstallTaskProgress != null) {
int max = 1024;
int progress = 0;
if (mInstallTaskProgress.totalPartitionNumber > 0) {
// totalProgressPercentage is defined iff totalPartitionNumber is defined
builder.setProgress(
100,
mInstallTaskProgress.totalProgressPercentage,
/* indeterminate = */ false);
} else {
int max = 1024;
int progress = 0;
int currentMax = max >> mInstallTaskProgress.partitionNumber;
progress = max - currentMax * 2;
int currentMax = max >> mInstallTaskProgress.partitionNumber;
progress = max - currentMax * 2;
long currentProgress =
(mInstallTaskProgress.installedSize >> 20)
* currentMax
/ Math.max(mInstallTaskProgress.partitionSize >> 20, 1);
long currentProgress =
(mInstallTaskProgress.installedBytes >> 20)
* currentMax
/ Math.max(mInstallTaskProgress.totalBytes >> 20, 1);
progress += (int) currentProgress;
progress += (int) currentProgress;
builder.setProgress(max, progress, false);
builder.setProgress(max, progress, false);
}
}
builder.addAction(new Notification.Action.Builder(
null, getString(R.string.notification_action_cancel),
@@ -609,10 +620,11 @@ public class DynamicSystemInstallationService extends Service
if (status == STATUS_IN_PROGRESS && mInstallTaskProgress != null) {
msg.append(
String.format(
", partition name: %s, progress: %d/%d",
", partition name: %s, progress: %d/%d, total_progress: %d%%",
mInstallTaskProgress.partitionName,
mInstallTaskProgress.installedSize,
mInstallTaskProgress.partitionSize));
mInstallTaskProgress.installedBytes,
mInstallTaskProgress.totalBytes,
mInstallTaskProgress.totalProgressPercentage));
}
if (detail != null) {
msg.append(", detail: " + detail);
@@ -639,7 +651,7 @@ public class DynamicSystemInstallationService extends Service
// TODO: send more info to the clients
if (mInstallTaskProgress != null) {
bundle.putLong(
DynamicSystemClient.KEY_INSTALLED_SIZE, mInstallTaskProgress.installedSize);
DynamicSystemClient.KEY_INSTALLED_SIZE, mInstallTaskProgress.installedBytes);
}
if (detail != null) {

View File

@@ -2,6 +2,6 @@
option java_package com.android.dynsystem
120000 dsu_progress_update (partition|3),(installed_size|2|5),(partition_size|2|5),(partition_number|1|5),(total_partition_number|1|5)
120000 dsu_progress_update (partition_name|3),(installed_bytes|2|5),(total_bytes|2|5),(partition_number|1|5),(total_partition_number|1|5),(total_progress_percentage|1|5)
120001 dsu_install_complete
120002 dsu_install_failed (cause|3)

View File

@@ -113,22 +113,25 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
static class Progress {
public final String partitionName;
public final long installedSize;
public final long partitionSize;
public final long installedBytes;
public final long totalBytes;
public final int partitionNumber;
public final int totalPartitionNumber;
public final int totalProgressPercentage;
Progress(
String partitionName,
long installedSize,
long partitionSize,
long installedBytes,
long totalBytes,
int partitionNumber,
int totalPartitionNumber) {
int totalPartitionNumber,
int totalProgressPercentage) {
this.partitionName = partitionName;
this.installedSize = installedSize;
this.partitionSize = partitionSize;
this.installedBytes = installedBytes;
this.totalBytes = totalBytes;
this.partitionNumber = partitionNumber;
this.totalPartitionNumber = totalPartitionNumber;
this.totalProgressPercentage = totalProgressPercentage;
}
}
@@ -149,20 +152,28 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
private final ProgressListener mListener;
private final boolean mIsNetworkUrl;
private final boolean mIsDeviceBootloaderUnlocked;
private final boolean mWantScratchPartition;
private DynamicSystemManager.Session mInstallationSession;
private KeyRevocationList mKeyRevocationList;
private boolean mIsZip;
private boolean mIsCompleted;
private String mPartitionName;
private long mPartitionSize;
private int mPartitionNumber;
private int mTotalPartitionNumber;
private InputStream mStream;
private ZipFile mZipFile;
private static final double PROGRESS_READONLY_PARTITION_WEIGHT = 0.8;
private static final double PROGRESS_WRITABLE_PARTITION_WEIGHT = 0.2;
private String mProgressPartitionName;
private long mProgressTotalBytes;
private int mProgressPartitionNumber;
private boolean mProgressPartitionIsReadonly;
private int mProgressCompletedReadonlyPartitions;
private int mProgressCompletedWritablePartitions;
private int mTotalReadonlyPartitions;
private int mTotalWritablePartitions;
private int mTotalPartitionNumber;
InstallationAsyncTask(
String url,
String dsuSlot,
@@ -193,21 +204,18 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
(pdbManager != null)
&& (pdbManager.getFlashLockState()
== PersistentDataBlockManager.FLASH_LOCK_UNLOCKED);
mWantScratchPartition = Build.IS_DEBUGGABLE;
}
@Override
protected Throwable doInBackground(String... voids) {
Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
final boolean wantScratchPartition = Build.IS_DEBUGGABLE;
try {
// call DynamicSystemManager to cleanup stuff
mDynSystem.remove();
verifyAndPrepare();
if (wantScratchPartition) {
++mTotalPartitionNumber;
}
mDynSystem.startInstallation(mDsuSlot);
@@ -226,7 +234,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
return null;
}
if (wantScratchPartition) {
if (mWantScratchPartition) {
// If host is debuggable, then install a scratch partition so that we can do
// adb remount in the guest system.
try {
@@ -290,14 +298,54 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
@Override
protected void onProgressUpdate(Long... installedSize) {
protected void onProgressUpdate(Long... progress) {
final long installedBytes = progress[0];
int totalProgressPercentage = 0;
if (mTotalPartitionNumber > 0) {
final double readonlyPartitionWeight =
mTotalReadonlyPartitions > 0
? PROGRESS_READONLY_PARTITION_WEIGHT / mTotalReadonlyPartitions
: 0;
final double writablePartitionWeight =
mTotalWritablePartitions > 0
? PROGRESS_WRITABLE_PARTITION_WEIGHT / mTotalWritablePartitions
: 0;
double totalProgress = 0.0;
if (mProgressTotalBytes > 0) {
totalProgress +=
(mProgressPartitionIsReadonly
? readonlyPartitionWeight
: writablePartitionWeight)
* installedBytes
/ mProgressTotalBytes;
}
totalProgress += readonlyPartitionWeight * mProgressCompletedReadonlyPartitions;
totalProgress += writablePartitionWeight * mProgressCompletedWritablePartitions;
totalProgressPercentage = (int) (totalProgress * 100);
}
mListener.onProgressUpdate(
new Progress(
mPartitionName,
installedSize[0],
mPartitionSize,
mPartitionNumber,
mTotalPartitionNumber));
mProgressPartitionName,
installedBytes,
mProgressTotalBytes,
mProgressPartitionNumber,
mTotalPartitionNumber,
totalProgressPercentage));
}
private void initPartitionProgress(String partitionName, long totalBytes, boolean readonly) {
if (mProgressPartitionNumber > 0) {
// Assume previous partition completed successfully.
if (mProgressPartitionIsReadonly) {
++mProgressCompletedReadonlyPartitions;
} else {
++mProgressCompletedWritablePartitions;
}
}
mProgressPartitionName = partitionName;
mProgressTotalBytes = totalBytes;
mProgressPartitionIsReadonly = readonly;
++mProgressPartitionNumber;
}
private void verifyAndPrepare() throws Exception {
@@ -314,16 +362,12 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
throw new UnsupportedFormatException(
String.format(Locale.US, "Unsupported file format: %s", mUrl));
}
// At least two partitions, {system, userdata}
mTotalPartitionNumber = 2;
if (mIsNetworkUrl) {
mStream = new URL(mUrl).openStream();
} else if (URLUtil.isFileUrl(mUrl)) {
if (mIsZip) {
mZipFile = new ZipFile(new File(new URL(mUrl).toURI()));
// {*.img in zip} + {userdata}
mTotalPartitionNumber = calculateNumberOfImagesInLocalZip(mZipFile) + 1;
} else {
mStream = new URL(mUrl).openStream();
}
@@ -334,6 +378,32 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
String.format(Locale.US, "Unsupported URL: %s", mUrl));
}
boolean hasTotalPartitionNumber = false;
if (mIsZip) {
if (mZipFile != null) {
// {*.img in zip} + {userdata}
hasTotalPartitionNumber = true;
mTotalReadonlyPartitions = calculateNumberOfImagesInLocalZip(mZipFile);
mTotalWritablePartitions = 1;
} else {
// TODO: Come up with a way to retrieve the number of total partitions from
// network URL.
}
} else {
// gzip has exactly two partitions, {system, userdata}
hasTotalPartitionNumber = true;
mTotalReadonlyPartitions = 1;
mTotalWritablePartitions = 1;
}
if (hasTotalPartitionNumber) {
if (mWantScratchPartition) {
// {scratch}
++mTotalWritablePartitions;
}
mTotalPartitionNumber = mTotalReadonlyPartitions + mTotalWritablePartitions;
}
try {
String listUrl = mContext.getString(R.string.key_revocation_list_url);
mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
@@ -370,9 +440,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
};
mPartitionName = partitionName;
mPartitionSize = partitionSize;
++mPartitionNumber;
initPartitionProgress(partitionName, partitionSize, /* readonly = */ false);
publishProgress(/* installedSize = */ 0L);
long prevInstalledSize = 0;
@@ -396,6 +464,10 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
}
if (prevInstalledSize != partitionSize) {
publishProgress(partitionSize);
}
if (mInstallationSession == null) {
throw new IOException(
"Failed to start installation with requested size: " + partitionSize);
@@ -559,9 +631,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
mInstallationSession.setAshmem(pfd, memoryFile.length());
mPartitionName = partitionName;
mPartitionSize = partitionSize;
++mPartitionNumber;
initPartitionProgress(partitionName, partitionSize, /* readonly = */ true);
publishProgress(/* installedSize = */ 0L);
long prevInstalledSize = 0;
@@ -588,6 +658,10 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
}
}
if (prevInstalledSize != partitionSize) {
publishProgress(partitionSize);
}
AvbPublicKey avbPublicKey = new AvbPublicKey();
if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) {
imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed"));