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

Commit 3a9690c3 authored by Yi-Yo Chiang's avatar Yi-Yo Chiang
Browse files

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
parent b344dfae
Loading
Loading
Loading
Loading
+37 −25
Original line number Diff line number Diff line
@@ -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,6 +489,13 @@ public class DynamicSystemInstallationService extends Service
                builder.setContentText(getString(R.string.notification_install_inprogress));

                if (mInstallTaskProgress != null) {
                    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;

@@ -493,14 +503,15 @@ public class DynamicSystemInstallationService extends Service
                        progress = max - currentMax * 2;

                        long currentProgress =
                            (mInstallTaskProgress.installedSize >> 20)
                                (mInstallTaskProgress.installedBytes >> 20)
                                        * currentMax
                                    / Math.max(mInstallTaskProgress.partitionSize >> 20, 1);
                                        / Math.max(mInstallTaskProgress.totalBytes >> 20, 1);

                        progress += (int) currentProgress;

                        builder.setProgress(max, progress, false);
                    }
                }
                builder.addAction(new Notification.Action.Builder(
                        null, getString(R.string.notification_action_cancel),
                        createPendingIntent(ACTION_CANCEL_INSTALL)).build());
@@ -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) {
+1 −1
Original line number Diff line number Diff line
@@ -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)
+108 −34
Original line number Diff line number Diff line
@@ -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"));