Merge changes I34d7ee3b,I99337ea2,I5c190938,If79073a9,Ie3343681 into qt-qpr1-dev
* changes: Reschedule the pre-reboot verification after boot completed. Prevent sessions from resuming once boot is completed Prevent extra sessions owned by staged install from living across restarts Prevent exceptions during staged install from crashing system server Prevent exceptions in pre-reboot verification from crashing system server
This commit is contained in:
committed by
Android (Google) Code Review
commit
71a7e46d2a
@@ -218,6 +218,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
|
|
||||||
public void systemReady() {
|
public void systemReady() {
|
||||||
mAppOps = mContext.getSystemService(AppOpsManager.class);
|
mAppOps = mContext.getSystemService(AppOpsManager.class);
|
||||||
|
mStagingManager.systemReady();
|
||||||
|
|
||||||
synchronized (mSessions) {
|
synchronized (mSessions) {
|
||||||
readSessionsLocked();
|
readSessionsLocked();
|
||||||
@@ -380,6 +381,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
Slog.w(TAG, "Abandoning old session created at "
|
Slog.w(TAG, "Abandoning old session created at "
|
||||||
+ session.createdMillis);
|
+ session.createdMillis);
|
||||||
valid = false;
|
valid = false;
|
||||||
|
} else if (isExtraSessionForStagedInstall(session)) {
|
||||||
|
valid = false;
|
||||||
} else {
|
} else {
|
||||||
valid = true;
|
valid = true;
|
||||||
}
|
}
|
||||||
@@ -410,6 +413,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extra sessions are created during staged install on temporary basis. They should not be
|
||||||
|
// allowed to live across system server restart.
|
||||||
|
private boolean isExtraSessionForStagedInstall(PackageInstallerSession session) {
|
||||||
|
return (session.params.installFlags & PackageManager.INSTALL_DRY_RUN) != 0
|
||||||
|
|| (session.params.installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("mSessions")
|
@GuardedBy("mSessions")
|
||||||
private void addHistoricalSessionLocked(PackageInstallerSession session) {
|
private void addHistoricalSessionLocked(PackageInstallerSession session) {
|
||||||
CharArrayWriter writer = new CharArrayWriter();
|
CharArrayWriter writer = new CharArrayWriter();
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ import android.annotation.Nullable;
|
|||||||
import android.apex.ApexInfo;
|
import android.apex.ApexInfo;
|
||||||
import android.apex.ApexInfoList;
|
import android.apex.ApexInfoList;
|
||||||
import android.apex.ApexSessionInfo;
|
import android.apex.ApexSessionInfo;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.IIntentReceiver;
|
import android.content.IIntentReceiver;
|
||||||
import android.content.IIntentSender;
|
import android.content.IIntentSender;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
@@ -44,6 +46,7 @@ import android.os.ParcelableException;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.apk.ApkSignatureVerifier;
|
import android.util.apk.ApkSignatureVerifier;
|
||||||
@@ -72,12 +75,16 @@ public class StagingManager {
|
|||||||
private final PackageInstallerService mPi;
|
private final PackageInstallerService mPi;
|
||||||
private final ApexManager mApexManager;
|
private final ApexManager mApexManager;
|
||||||
private final PowerManager mPowerManager;
|
private final PowerManager mPowerManager;
|
||||||
|
private final Context mContext;
|
||||||
private final Handler mBgHandler;
|
private final Handler mBgHandler;
|
||||||
|
private PackageInstallerSession mPendingSession;
|
||||||
|
private boolean mIsReady;
|
||||||
|
|
||||||
@GuardedBy("mStagedSessions")
|
@GuardedBy("mStagedSessions")
|
||||||
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
|
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
|
||||||
|
|
||||||
StagingManager(PackageInstallerService pi, ApexManager am, Context context) {
|
StagingManager(PackageInstallerService pi, ApexManager am, Context context) {
|
||||||
|
mContext = context;
|
||||||
mPi = pi;
|
mPi = pi;
|
||||||
mApexManager = am;
|
mApexManager = am;
|
||||||
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
@@ -202,6 +209,12 @@ public class StagingManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void preRebootVerification(@NonNull PackageInstallerSession session) {
|
private void preRebootVerification(@NonNull PackageInstallerSession session) {
|
||||||
|
try {
|
||||||
|
if (!mIsReady) {
|
||||||
|
mPendingSession = session;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
|
|
||||||
final ApexInfoList apexInfoList = new ApexInfoList();
|
final ApexInfoList apexInfoList = new ApexInfoList();
|
||||||
@@ -247,11 +260,12 @@ public class StagingManager {
|
|||||||
for (ApexInfo apexPackage : apexInfoList.apexInfos) {
|
for (ApexInfo apexPackage : apexInfoList.apexInfos) {
|
||||||
if (!validateApexSignature(apexPackage.packagePath,
|
if (!validateApexSignature(apexPackage.packagePath,
|
||||||
apexPackage.packageName)) {
|
apexPackage.packageName)) {
|
||||||
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
session.setStagedSessionFailed(
|
||||||
|
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||||
"APK-container signature verification failed for package "
|
"APK-container signature verification failed for package "
|
||||||
+ apexPackage.packageName + ". Signature of file "
|
+ apexPackage.packageName + ". Signature of file "
|
||||||
+ apexPackage.packagePath + " does not match the signature of "
|
+ apexPackage.packagePath + " does not match the signature"
|
||||||
+ " the package already installed.");
|
+ " of the package already installed.");
|
||||||
// TODO(b/118865310): abort the session on apexd.
|
// TODO(b/118865310): abort the session on apexd.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -260,8 +274,8 @@ public class StagingManager {
|
|||||||
|
|
||||||
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
|
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
|
||||||
// If rollback is enabled for this session, we call through to the RollbackManager
|
// If rollback is enabled for this session, we call through to the RollbackManager
|
||||||
// with the list of sessions it must enable rollback for. Note that notifyStagedSession
|
// with the list of sessions it must enable rollback for. Note that
|
||||||
// is a synchronous operation.
|
// notifyStagedSession is a synchronous operation.
|
||||||
final IRollbackManager rm = IRollbackManager.Stub.asInterface(
|
final IRollbackManager rm = IRollbackManager.Stub.asInterface(
|
||||||
ServiceManager.getService(Context.ROLLBACK_SERVICE));
|
ServiceManager.getService(Context.ROLLBACK_SERVICE));
|
||||||
try {
|
try {
|
||||||
@@ -282,6 +296,11 @@ public class StagingManager {
|
|||||||
"APEX staging failed, check logcat messages from apexd for more "
|
"APEX staging failed, check logcat messages from apexd for more "
|
||||||
+ "details.");
|
+ "details.");
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Slog.e(TAG, "Pre-reboot verification failed due to unhandled exception", e);
|
||||||
|
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||||
|
"Pre-reboot verification failed due to unhandled exception: " + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -346,14 +365,23 @@ public class StagingManager {
|
|||||||
}
|
}
|
||||||
// The APEX part of the session is activated, proceed with the installation of APKs.
|
// The APEX part of the session is activated, proceed with the installation of APKs.
|
||||||
if (!installApksInSession(session, /* preReboot */ false)) {
|
if (!installApksInSession(session, /* preReboot */ false)) {
|
||||||
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
onInstallationFailure(session, new PackageManagerException(
|
||||||
"Staged installation of APKs failed. Check logcat messages for"
|
SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Staged installation of APKs "
|
||||||
+ "more information.");
|
+ "failed. Check logcat messages for more information."));
|
||||||
|
|
||||||
if (!hasApex) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session.setStagedSessionApplied();
|
||||||
|
if (hasApex) {
|
||||||
|
mApexManager.markStagedSessionSuccessful(session.sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onInstallationFailure(PackageInstallerSession session, PackageManagerException e) {
|
||||||
|
session.setStagedSessionFailed(e.error, e.getMessage());
|
||||||
|
if (!sessionContainsApex(session)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!mApexManager.abortActiveSession()) {
|
if (!mApexManager.abortActiveSession()) {
|
||||||
Slog.e(TAG, "Failed to abort APEXd session");
|
Slog.e(TAG, "Failed to abort APEXd session");
|
||||||
} else {
|
} else {
|
||||||
@@ -362,13 +390,6 @@ public class StagingManager {
|
|||||||
+ "to the previous state of APEXd.");
|
+ "to the previous state of APEXd.");
|
||||||
mPowerManager.reboot(null);
|
mPowerManager.reboot(null);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.setStagedSessionApplied();
|
|
||||||
if (hasApex) {
|
|
||||||
mApexManager.markStagedSessionSuccessful(session.sessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> findAPKsInDir(File stageDir) {
|
private List<String> findAPKsInDir(File stageDir) {
|
||||||
@@ -648,6 +669,11 @@ public class StagingManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkStateAndResume(@NonNull PackageInstallerSession session) {
|
private void checkStateAndResume(@NonNull PackageInstallerSession session) {
|
||||||
|
// Do not resume session if boot completed already
|
||||||
|
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!session.isCommitted()) {
|
if (!session.isCommitted()) {
|
||||||
// Session hasn't been committed yet, ignore.
|
// Session hasn't been committed yet, ignore.
|
||||||
return;
|
return;
|
||||||
@@ -664,7 +690,37 @@ public class StagingManager {
|
|||||||
} else {
|
} else {
|
||||||
// Session had already being marked ready. Start the checks to verify if there is any
|
// Session had already being marked ready. Start the checks to verify if there is any
|
||||||
// follow-up work.
|
// follow-up work.
|
||||||
|
try {
|
||||||
resumeSession(session);
|
resumeSession(session);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Slog.e(TAG, "Staged install failed due to unhandled exception", e);
|
||||||
|
onInstallationFailure(session, new PackageManagerException(
|
||||||
|
SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||||
|
"Staged install failed due to unhandled exception: " + e));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void systemReady() {
|
||||||
|
// Register the receiver of boot completed intent for staging manager.
|
||||||
|
mContext.registerReceiver(new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context ctx, Intent intent) {
|
||||||
|
readyToStart();
|
||||||
|
ctx.unregisterReceiver(this);
|
||||||
|
}
|
||||||
|
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the handler that system is ready, and reschedule the pre-reboot verifications.
|
||||||
|
private synchronized void readyToStart() {
|
||||||
|
mIsReady = true;
|
||||||
|
if (mPendingSession != null) {
|
||||||
|
mBgHandler.post(() -> {
|
||||||
|
preRebootVerification(mPendingSession);
|
||||||
|
mPendingSession = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user