diff options
6 files changed, 63 insertions, 17 deletions
diff --git a/api/current.txt b/api/current.txt index de5085e0b563..08b0e0385b8c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11431,6 +11431,7 @@ package android.content.pm { method public int getSessionId(); method public long getSize(); method public int getStagedSessionErrorCode(); + method public String getStagedSessionErrorMessage(); method public boolean isActive(); method public boolean isMultiPackage(); method public boolean isSealed(); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 73b1f4e7e536..2dc014c45fad 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1710,6 +1710,7 @@ public class PackageInstaller { /** {@hide} */ public boolean isSessionFailed; private int mStagedSessionErrorCode; + private String mStagedSessionErrorMessage; /** {@hide} */ @UnsupportedAppUsage @@ -1749,6 +1750,7 @@ public class PackageInstaller { isSessionReady = source.readBoolean(); isSessionFailed = source.readBoolean(); mStagedSessionErrorCode = source.readInt(); + mStagedSessionErrorMessage = source.readString(); } /** @@ -2066,9 +2068,19 @@ public class PackageInstaller { return mStagedSessionErrorCode; } + /** + * Text description of the error code returned by {@code getStagedSessionErrorCode}, or + * empty string if no error was encountered. + */ + public String getStagedSessionErrorMessage() { + return mStagedSessionErrorMessage; + } + /** {@hide} */ - public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode) { + public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode, + String errorMessage) { mStagedSessionErrorCode = errorCode; + mStagedSessionErrorMessage = errorMessage; } @Override @@ -2106,6 +2118,7 @@ public class PackageInstaller { dest.writeBoolean(isSessionReady); dest.writeBoolean(isSessionFailed); dest.writeInt(mStagedSessionErrorCode); + dest.writeString(mStagedSessionErrorMessage); } public static final Parcelable.Creator<SessionInfo> diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0ab2a7361ac0..eab5c8f866a8 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -537,7 +537,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this, mInstallThread.getLooper(), mStagingManager, sessionId, userId, installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false, - false, null, SessionInfo.INVALID_ID, false, false, false, SessionInfo.NO_ERROR); + false, null, SessionInfo.INVALID_ID, false, false, false, SessionInfo.NO_ERROR, + ""); synchronized (mSessions) { mSessions.put(sessionId, session); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index b8825bbd2d72..494ec3ff67aa 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -155,6 +155,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String ATTR_IS_FAILED = "isFailed"; private static final String ATTR_IS_APPLIED = "isApplied"; private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode"; + private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage"; private static final String ATTR_MODE = "mode"; private static final String ATTR_INSTALL_FLAGS = "installFlags"; private static final String ATTR_INSTALL_LOCATION = "installLocation"; @@ -267,6 +268,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private boolean mStagedSessionFailed; @GuardedBy("mLock") private int mStagedSessionErrorCode = SessionInfo.NO_ERROR; + @GuardedBy("mLock") + private String mStagedSessionErrorMessage; /** * Path to the validated base APK for this session, which may point at an @@ -413,7 +416,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { String installerPackageName, int installerUid, SessionParams params, long createdMillis, File stageDir, String stageCid, boolean prepared, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, - boolean isFailed, boolean isApplied, int stagedSessionErrorCode) { + boolean isFailed, boolean isApplied, int stagedSessionErrorCode, + String stagedSessionErrorMessage) { mCallback = callback; mContext = context; mPm = pm; @@ -447,6 +451,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionFailed = isFailed; mStagedSessionApplied = isApplied; mStagedSessionErrorCode = stagedSessionErrorCode; + mStagedSessionErrorMessage = + stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; if (sealed) { synchronized (mLock) { try { @@ -499,7 +505,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.isSessionApplied = mStagedSessionApplied; info.isSessionReady = mStagedSessionReady; info.isSessionFailed = mStagedSessionFailed; - info.setStagedSessionErrorCode(mStagedSessionErrorCode); + info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage); } return info; } @@ -1971,17 +1977,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionApplied = false; mStagedSessionFailed = false; mStagedSessionErrorCode = SessionInfo.NO_ERROR; + mStagedSessionErrorMessage = ""; } mCallback.onStagedSessionChanged(this); } /** {@hide} */ - void setStagedSessionFailed(@StagedSessionErrorCode int errorCode) { + void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, + String errorMessage) { synchronized (mLock) { mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; mStagedSessionErrorCode = errorCode; + mStagedSessionErrorMessage = errorMessage; + Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage); } mCallback.onStagedSessionChanged(this); } @@ -1993,6 +2003,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionApplied = true; mStagedSessionFailed = false; mStagedSessionErrorCode = SessionInfo.NO_ERROR; + mStagedSessionErrorMessage = ""; } mCallback.onStagedSessionChanged(this); } @@ -2017,6 +2028,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return mStagedSessionErrorCode; } + /** {@hide} */ + String getStagedSessionErrorMessage() { + return mStagedSessionErrorMessage; + } + private void destroyInternal() { synchronized (mLock) { mSealed = true; @@ -2133,6 +2149,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed); writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied); writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode); + writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE, + mStagedSessionErrorMessage); // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after // we've read all sessions. writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId); @@ -2253,6 +2271,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED); final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE, SessionInfo.NO_ERROR); + final String stagedSessionErrorMessage = readStringAttribute(in, + ATTR_STAGED_SESSION_ERROR_MESSAGE); if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) { throw new IllegalArgumentException("Can't restore staged session with invalid state."); @@ -2296,7 +2316,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { installerThread, stagingManager, sessionId, userId, installerPackageName, installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, - stagedSessionErrorCode); + stagedSessionErrorCode, stagedSessionErrorMessage); } /** diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 5311c2a55931..b930d267282f 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -166,6 +166,7 @@ public class StagingManager { if (!session.isMultiPackage() && isApexSession(session)) { success = submitSessionToApexService(session, null, apexInfoList); + } else if (session.isMultiPackage()) { List<PackageInstallerSession> childSessions = Arrays.stream(session.getChildSessionIds()) @@ -179,7 +180,13 @@ public class StagingManager { } // else this is a staged multi-package session with no APEX files. } - if (success && (apexInfoList.apexInfos.length > 0)) { + if (!success) { + session.setStagedSessionFailed( + SessionInfo.VERIFICATION_FAILED, + "APEX staging failed, check logcat messages from apexd for more details."); + } + + if (apexInfoList.apexInfos.length > 0) { // For APEXes, we validate the signature here before we mark the session as ready, // so we fail the session early if there is a signature mismatch. For APKs, the // signature verification will be done by the package manager at the point at which @@ -190,17 +197,16 @@ public class StagingManager { for (ApexInfo apexPackage : apexInfoList.apexInfos) { if (!validateApexSignatureLocked(apexPackage.packagePath, apexPackage.packageName)) { - success = false; - break; + session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED, + "APK-container signature verification failed for package " + + apexPackage.packageName + ". Signature of file " + + apexPackage.packagePath + " does not match the signature of " + + " the package already installed."); + return; } } } - - if (success) { - session.setStagedSessionReady(); - } else { - session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED); - } + session.setStagedSessionReady(); } private void resumeSession(@NonNull PackageInstallerSession session) { @@ -217,7 +223,9 @@ public class StagingManager { return; } if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { - session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED); + session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED, + "APEX activation failed. Check logcat messages from apexd for " + + "more information."); } if (apexSessionInfo.isActivated) { session.setStagedSessionApplied(); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java index 73e96134167a..742ae41ed9f0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java @@ -173,7 +173,8 @@ public class PackageInstallerSessionTest { /* isReady */ staged ? true : false, /* isFailed */ false, /* isApplied */false, - /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.VERIFICATION_FAILED); + /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.VERIFICATION_FAILED, + /* stagedSessionErrorMessage */ "some error"); } private void dumpSession(PackageInstallerSession session) { @@ -295,6 +296,8 @@ public class PackageInstallerSessionTest { assertEquals(expected.isStagedSessionFailed(), actual.isStagedSessionFailed()); assertEquals(expected.isStagedSessionReady(), actual.isStagedSessionReady()); assertEquals(expected.getStagedSessionErrorCode(), actual.getStagedSessionErrorCode()); + assertEquals(expected.getStagedSessionErrorMessage(), + actual.getStagedSessionErrorMessage()); assertEquals(expected.isPrepared(), actual.isPrepared()); assertEquals(expected.isSealed(), actual.isSealed()); assertEquals(expected.isMultiPackage(), actual.isMultiPackage()); |