Propagate error while scanning apk-in-apex to session object

By storing the error message on session object, we avoid searching the
logcat (which is not always available) for information on why
apk-in-apex scanning failed.

Bug: 178192690
Test: atest ApexManagerTest
Test: atest StagedInstallTest#testApexFailsToInstallIfApkInApexFailsToScan
Change-Id: I1be52da133ea534ba103f5fbdea5b8138ef67ef6
This commit is contained in:
Samiul Islam
2021-06-30 16:30:22 +01:00
parent 3f32db86fb
commit dde0b33636
5 changed files with 42 additions and 26 deletions

View File

@@ -67,6 +67,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -306,17 +307,19 @@ public abstract class ApexManager {
/**
* Reports error raised during installation of apk-in-apex.
*
* @param scanDir the directory of the apex inside which apk-in-apex resides.
* @param scanDirPath the directory of the apex inside which apk-in-apex resides.
* @param errorMsg the actual error that occurred when scanning the path
*/
abstract void reportErrorWithApkInApex(String scanDirPath);
abstract void reportErrorWithApkInApex(String scanDirPath, String errorMsg);
/**
* Returns true if there were no errors when installing apk-in-apex inside
* {@param apexPackageName}, otherwise false.
* Returns null if there were no errors when installing apk-in-apex inside
* {@param apexPackageName}, otherwise returns the error as string
*
* @param apexPackageName Package name of the apk container of apex
*/
abstract boolean isApkInApexInstallSuccess(String apexPackageName);
@Nullable
abstract String getApkInApexInstallError(String apexPackageName);
/**
* Returns list of {@code packageName} of apks inside the given apex.
@@ -438,7 +441,7 @@ public abstract class ApexManager {
* inside {@code apexModuleName}.
*/
@GuardedBy("mLock")
private Set<String> mErrorWithApkInApex = new ArraySet<>();
private Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
@GuardedBy("mLock")
private List<PackageInfo> mAllPackagesCache;
@@ -841,26 +844,27 @@ public abstract class ApexManager {
}
@Override
void reportErrorWithApkInApex(String scanDirPath) {
void reportErrorWithApkInApex(String scanDirPath, String errorMsg) {
synchronized (mLock) {
for (ActiveApexInfo aai : mActiveApexInfosCache) {
if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) {
mErrorWithApkInApex.add(aai.apexModuleName);
mErrorWithApkInApex.put(aai.apexModuleName, errorMsg);
}
}
}
}
@Override
boolean isApkInApexInstallSuccess(String apexPackageName) {
@Nullable
String getApkInApexInstallError(String apexPackageName) {
synchronized (mLock) {
Preconditions.checkState(mPackageNameToApexModuleName != null,
"APEX packages have not been scanned");
String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
if (moduleName == null) {
return false;
return null;
}
return !mErrorWithApkInApex.contains(moduleName);
return mErrorWithApkInApex.get(moduleName);
}
}
@@ -1273,13 +1277,14 @@ public abstract class ApexManager {
}
@Override
void reportErrorWithApkInApex(String scanDirPath) {
void reportErrorWithApkInApex(String scanDirPath, String errorMsg) {
// No-op
}
@Override
boolean isApkInApexInstallSuccess(String apexPackageName) {
return true;
@Nullable
String getApkInApexInstallError(String apexPackageName) {
return null;
}
@Override

View File

@@ -11824,6 +11824,7 @@ public class PackageManagerService extends IPackageManager.Stub
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
@@ -11836,20 +11837,22 @@ public class PackageManagerService extends IPackageManager.Stub
currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
}
} else if (throwable instanceof PackageParserException) {
PackageParserException e = (PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath());
mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps

View File

@@ -425,9 +425,10 @@ public class StagingManager {
for (StagedSession apexSession : apexSessions) {
String packageName = apexSession.getPackageName();
if (!mApexManager.isApkInApexInstallSuccess(packageName)) {
String errorMsg = mApexManager.getApkInApexInstallError(packageName);
if (errorMsg != null) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"Failed to install apk-in-apex of " + packageName);
"Failed to install apk-in-apex of " + packageName + " : " + errorMsg);
}
}
}
@@ -955,12 +956,17 @@ public class StagingManager {
continue;
} else if (isApexSessionFailed(apexSession)) {
hasFailedApexSession = true;
String errorMsg = "APEX activation failed. " + apexSession.errorMessage;
if (!TextUtils.isEmpty(apexSession.crashingNativeProcess)) {
prepareForLoggingApexdRevert(session, apexSession.crashingNativeProcess);
errorMsg = "Session reverted due to crashing native process: "
+ apexSession.crashingNativeProcess;
}
String errorMsg = "APEX activation failed.";
final String reasonForRevert = getReasonForRevert();
if (!TextUtils.isEmpty(reasonForRevert)) {
errorMsg += " Reason: " + reasonForRevert;
} else if (!TextUtils.isEmpty(apexSession.errorMessage)) {
errorMsg += " Error: " + apexSession.errorMessage;
}
Slog.d(TAG, errorMsg);
session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
continue;
} else if (apexSession.isActivated || apexSession.isSuccess) {

View File

@@ -311,7 +311,7 @@ public class StagingManagerTest {
assertThat(apexSession1.getErrorCode())
.isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. "
+ "Failed for test");
+ "Error: Failed for test");
assertThat(apexSession2.getErrorCode())
.isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);

View File

@@ -286,9 +286,11 @@ public class ApexManagerTest {
mApexManager.scanApexPackagesTraced(mPackageParser2,
ParallelPackageParser.makeExecutorService());
assertThat(mApexManager.isApkInApexInstallSuccess(activeApex.apexModuleName)).isTrue();
mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath());
assertThat(mApexManager.isApkInApexInstallSuccess(activeApex.apexModuleName)).isFalse();
assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull();
mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(),
"Some random error");
assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName))
.isEqualTo("Some random error");
}
/**