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
This commit is contained in:
Yi-Yo Chiang 2022-03-31 18:18:54 +08:00
parent c32215ee94
commit c88a3d9be4
6 changed files with 93 additions and 41 deletions

View File

@ -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<Integer, Session> 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());

View File

@ -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.

View File

@ -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);
}

View File

@ -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

View File

@ -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<String, Long, Throwable> {
}
}
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<String, Long, Throwable> {
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<String, Long, Throwable> {
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<String, Long, Throwable> {
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<Integer, DynamicSystemManager.Session> 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<String, Long, Throwable> {
}
}
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<String, Long, Throwable> {
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<String, Long, Throwable> {
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<Integer, DynamicSystemManager.Session> 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<String, Long, Throwable> {
}
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<String, Long, Throwable> {
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<String, Long, Throwable> {
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) {

View File

@ -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