From c88a3d9be41686c83c63a57dd80fb81d2478b7ee Mon Sep 17 00:00:00 2001 From: Yi-Yo Chiang Date: Thu, 31 Mar 2022 18:18:54 +0800 Subject: [PATCH] DSU service: Log insufficient storage space error Log a specialized error message if installation task failed due to insufficient storage space. This helps the user to disgnose the source of error. Bug: 200002443 Test: start DSU task and check logcat Change-Id: Iabb3e0325ae99c343978ca6c35ab8378f20e0527 --- .../os/image/DynamicSystemManager.java | 18 ++-- .../os/image/IDynamicSystemService.aidl | 4 +- .../DynamicSystemInstallationService.java | 7 ++ .../android/dynsystem/EventLogTags.logtags | 1 + .../dynsystem/InstallationAsyncTask.java | 93 +++++++++++++------ .../android/server/DynamicSystemService.java | 11 +-- 6 files changed, 93 insertions(+), 41 deletions(-) diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java index e8e47857ecba..9610b16a312c 100644 --- a/core/java/android/os/image/DynamicSystemManager.java +++ b/core/java/android/os/image/DynamicSystemManager.java @@ -16,13 +16,16 @@ package android.os.image; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.gsi.AvbPublicKey; import android.gsi.GsiProgress; +import android.gsi.IGsiService; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.util.Pair; /** * The DynamicSystemManager offers a mechanism to use a new system image temporarily. After the @@ -138,17 +141,18 @@ public class DynamicSystemManager { * @param name The DSU partition name * @param size Size of the DSU image in bytes * @param readOnly True if the partition is read only, e.g. system. - * @return {@code true} if the call succeeds. {@code false} either the device does not contain - * enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be - * true. + * @return {@code Integer} an IGsiService.INSTALL_* status code. {@link Session} an installation + * session object if successful, otherwise {@code null}. */ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) - public Session createPartition(String name, long size, boolean readOnly) { + public @NonNull Pair createPartition( + String name, long size, boolean readOnly) { try { - if (mService.createPartition(name, size, readOnly)) { - return new Session(); + int status = mService.createPartition(name, size, readOnly); + if (status == IGsiService.INSTALL_OK) { + return new Pair<>(status, new Session()); } else { - return null; + return new Pair<>(status, null); } } catch (RemoteException e) { throw new RuntimeException(e.toString()); diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl index 4e69952fac2f..755368a85c40 100644 --- a/core/java/android/os/image/IDynamicSystemService.aidl +++ b/core/java/android/os/image/IDynamicSystemService.aidl @@ -35,10 +35,10 @@ interface IDynamicSystemService * @param name The DSU partition name * @param size Size of the DSU image in bytes * @param readOnly True if this partition is readOnly - * @return true if the call succeeds + * @return IGsiService.INSTALL_* status code */ @EnforcePermission("MANAGE_DYNAMIC_SYSTEM") - boolean createPartition(@utf8InCpp String name, long size, boolean readOnly); + int createPartition(@utf8InCpp String name, long size, boolean readOnly); /** * Complete the current partition installation. diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 3a3565941489..55626844594d 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -110,6 +110,7 @@ public class DynamicSystemInstallationService extends Service private static final int EVENT_DSU_PROGRESS_UPDATE = 120000; private static final int EVENT_DSU_INSTALL_COMPLETE = 120001; private static final int EVENT_DSU_INSTALL_FAILED = 120002; + private static final int EVENT_DSU_INSTALL_INSUFFICIENT_SPACE = 120003; protected static void logEventProgressUpdate( String partitionName, @@ -136,6 +137,10 @@ public class DynamicSystemInstallationService extends Service EventLog.writeEvent(EVENT_DSU_INSTALL_FAILED, cause); } + protected static void logEventInsufficientSpace() { + EventLog.writeEvent(EVENT_DSU_INSTALL_INSUFFICIENT_SPACE); + } + /* * IPC */ @@ -258,6 +263,8 @@ public class DynamicSystemInstallationService extends Service if (result == RESULT_CANCELLED) { logEventFailed("Dynamic System installation task is canceled by the user."); + } else if (detail instanceof InstallationAsyncTask.InsufficientSpaceException) { + logEventInsufficientSpace(); } else { logEventFailed("error: " + detail); } diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags index 407314384be8..8d8be1030b8e 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags @@ -5,3 +5,4 @@ option java_package com.android.dynsystem 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) +120003 dsu_install_insufficient_space diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java index 38d851eb76aa..62e53d62fd2a 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java @@ -18,6 +18,7 @@ package com.android.dynsystem; import android.content.Context; import android.gsi.AvbPublicKey; +import android.gsi.IGsiService; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -27,6 +28,7 @@ import android.os.SystemProperties; import android.os.image.DynamicSystemManager; import android.service.persistentdata.PersistentDataBlockManager; import android.util.Log; +import android.util.Pair; import android.util.Range; import android.webkit.URLUtil; @@ -106,8 +108,15 @@ class InstallationAsyncTask extends AsyncTask { } } + static class InsufficientSpaceException extends IOException { + InsufficientSpaceException(String message) { + super(message); + } + } + /** UNSET means the installation is not completed */ static final int RESULT_UNSET = 0; + static final int RESULT_OK = 1; static final int RESULT_CANCELLED = 2; static final int RESULT_ERROR_IO = 3; @@ -157,6 +166,7 @@ class InstallationAsyncTask extends AsyncTask { private final boolean mIsNetworkUrl; private final boolean mIsDeviceBootloaderUnlocked; private final boolean mWantScratchPartition; + private int mCreatePartitionStatus; private DynamicSystemManager.Session mInstallationSession; private KeyRevocationList mKeyRevocationList; @@ -364,7 +374,7 @@ class InstallationAsyncTask extends AsyncTask { mIsZip = true; } else { throw new UnsupportedFormatException( - String.format(Locale.US, "Unsupported file format: %s", mUrl)); + String.format(Locale.US, "Unsupported file format: %s", mUrl)); } if (mIsNetworkUrl) { @@ -435,14 +445,19 @@ class InstallationAsyncTask extends AsyncTask { throws IOException { Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize); - Thread thread = new Thread() { - @Override - public void run() { - mInstallationSession = - mDynSystem.createPartition( - partitionName, partitionSize, /* readOnly= */ false); - } - }; + mCreatePartitionStatus = 0; + mInstallationSession = null; + Thread thread = + new Thread() { + @Override + public void run() { + Pair result = + mDynSystem.createPartition( + partitionName, partitionSize, /* readOnly = */ false); + mCreatePartitionStatus = result.first; + mInstallationSession = result.second; + } + }; initPartitionProgress(partitionName, partitionSize, /* readonly = */ false); publishProgress(/* installedSize = */ 0L); @@ -468,13 +483,17 @@ class InstallationAsyncTask extends AsyncTask { } } - if (prevInstalledSize != partitionSize) { - publishProgress(partitionSize); - } - if (mInstallationSession == null) { - throw new IOException( - "Failed to start installation with requested size: " + partitionSize); + if (mCreatePartitionStatus == IGsiService.INSTALL_ERROR_NO_SPACE + || mCreatePartitionStatus == IGsiService.INSTALL_ERROR_FILE_SYSTEM_CLUTTERED) { + throw new InsufficientSpaceException( + "Failed to create " + + partitionName + + " partition: storage media has insufficient free space"); + } else { + throw new IOException( + "Failed to start installation with requested size: " + partitionSize); + } } // Reset installation session and verify that installation completes successfully. @@ -482,6 +501,11 @@ class InstallationAsyncTask extends AsyncTask { if (!mDynSystem.closePartition()) { throw new IOException("Failed to complete partition installation: " + partitionName); } + + // Ensure a 100% mark is published. + if (prevInstalledSize != partitionSize) { + publishProgress(partitionSize); + } } private void installScratch() throws IOException { @@ -606,10 +630,19 @@ class InstallationAsyncTask extends AsyncTask { throw new IOException("Cannot get raw size for " + partitionName); } - Thread thread = new Thread(() -> { - mInstallationSession = - mDynSystem.createPartition(partitionName, partitionSize, true); - }); + mCreatePartitionStatus = 0; + mInstallationSession = null; + Thread thread = + new Thread() { + @Override + public void run() { + Pair result = + mDynSystem.createPartition( + partitionName, partitionSize, /* readOnly = */ true); + mCreatePartitionStatus = result.first; + mInstallationSession = result.second; + } + }; Log.d(TAG, "Start creating partition: " + partitionName); thread.start(); @@ -627,8 +660,16 @@ class InstallationAsyncTask extends AsyncTask { } if (mInstallationSession == null) { - throw new IOException( - "Failed to start installation with requested size: " + partitionSize); + if (mCreatePartitionStatus == IGsiService.INSTALL_ERROR_NO_SPACE + || mCreatePartitionStatus == IGsiService.INSTALL_ERROR_FILE_SYSTEM_CLUTTERED) { + throw new InsufficientSpaceException( + "Failed to create " + + partitionName + + " partition: storage media has insufficient free space"); + } else { + throw new IOException( + "Failed to start installation with requested size: " + partitionSize); + } } Log.d(TAG, "Start installing: " + partitionName); @@ -688,11 +729,6 @@ class InstallationAsyncTask extends AsyncTask { installedSize += numBytesRead; } - // Ensure a 100% mark is published. - if (prevInstalledSize != partitionSize) { - publishProgress(partitionSize); - } - AvbPublicKey avbPublicKey = new AvbPublicKey(); if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) { imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed")); @@ -708,6 +744,11 @@ class InstallationAsyncTask extends AsyncTask { if (!mDynSystem.closePartition()) { throw new IOException("Failed to complete partition installation: " + partitionName); } + + // Ensure a 100% mark is published. + if (prevInstalledSize != partitionSize) { + publishProgress(partitionSize); + } } private static String toHexString(byte[] bytes) { diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index e924012c8892..99e12a8f9f55 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -119,14 +119,13 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { @Override @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) - public boolean createPartition(String name, long size, boolean readOnly) - throws RemoteException { + public int createPartition(String name, long size, boolean readOnly) throws RemoteException { IGsiService service = getGsiService(); - if (service.createPartition(name, size, readOnly) != 0) { - Slog.i(TAG, "Failed to install " + name); - return false; + int status = service.createPartition(name, size, readOnly); + if (status != IGsiService.INSTALL_OK) { + Slog.i(TAG, "Failed to create partition: " + name); } - return true; + return status; } @Override