From 692e12085835925d4e34607032e4f290502394fc Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Thu, 16 Jan 2020 18:38:17 +0800 Subject: [PATCH] Fixed NPE in package installer session. We updated staged sessions to activation failed state when they aren't in terminal state, and device received ota and reboot. This happend before the StagingManager resumes the sessions when the whole set of parent+child sessions have been restored. A parent session probably cannot find the child session, and a null exception could happen. In this change, we do not destroy child sessions before we destroy the parent session. We only destroy a child session directly if we are sure that its parent session doesn't exist. Bug: 147651771 Test: StagedInstallTest Change-Id: Iac6489a04df35f851aa18a91e1dde2d73928b8ec Merged-in: Iac6489a04df35f851aa18a91e1dde2d73928b8ec (cherry picked from commit 1fc8b36cf38e2632db68e5d15acd3ff83d1f54aa) --- .../com/android/server/pm/PackageInstallerService.java | 9 ++++++--- .../core/java/com/android/server/pm/StagingManager.java | 9 ++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index fd8db4b99be85..d17365db77dd5 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -258,12 +258,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK // atomic install which needs to query sessions, which requires lock on mSessions. + boolean isDeviceUpgrading = mPm.isDeviceUpgrading(); for (PackageInstallerSession session : stagedSessionsToRestore) { - if (mPm.isDeviceUpgrading() && !session.isStagedAndInTerminalState()) { + if (!session.isStagedAndInTerminalState() && session.hasParentSessionId() + && getSession(session.getParentSessionId()) == null) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - "Build fingerprint has changed"); + "An orphan staged session " + session.sessionId + " is found, " + + "parent " + session.getParentSessionId() + " is missing"); } - mStagingManager.restoreSession(session); + mStagingManager.restoreSession(session, isDeviceUpgrading); } // Broadcasts are not sent while we restore sessions on boot, since no processes would be // ready to listen to them. From now on, we greedily assume that broadcasts requests are diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 9913c9846a17c..895d2c5d00bf3 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -619,7 +619,7 @@ public class StagingManager { return false; } - void restoreSession(@NonNull PackageInstallerSession session) { + void restoreSession(@NonNull PackageInstallerSession session, boolean isDeviceUpgrading) { PackageInstallerSession sessionToResume = session; synchronized (mStagedSessions) { mStagedSessions.append(session.sessionId, session); @@ -636,6 +636,13 @@ public class StagingManager { } } } + // The preconditions used during pre-reboot verification might have changed when device + // is upgrading. Updated staged sessions to activation failed before we resume the session. + if (isDeviceUpgrading && !sessionToResume.isStagedAndInTerminalState()) { + sessionToResume.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "Build fingerprint has changed"); + return; + } checkStateAndResume(sessionToResume); }