diff options
| author | 2009-08-25 00:27:12 -0700 | |
|---|---|---|
| committer | 2009-08-25 15:15:31 -0700 | |
| commit | 9acc030be474db2bc2371440a12cbdc478238689 (patch) | |
| tree | bdf84962a21ad6bce0c74d91cef7d07e629df787 | |
| parent | 1e09e9e0d09602ee84a299f09b451db686ff6c40 (diff) | |
Implement issue #2071750: add a pre-boot upgrade step.
Change-Id: I7133a03a6bf302dbac00c5f8b0420071d2453f89
| -rw-r--r-- | core/java/android/content/Intent.java | 20 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 199 |
2 files changed, 177 insertions, 42 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 496ab76d5921..9dedca6ac612 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1696,7 +1696,6 @@ public class Intent implements Parcelable { public static final String ACTION_REBOOT = "android.intent.action.REBOOT"; - /** * Broadcast Action: a remote intent is to be broadcasted. * @@ -1711,6 +1710,18 @@ public class Intent implements Parcelable { public static final String ACTION_REMOTE_INTENT = "android.intent.action.REMOTE_INTENT"; + /** + * Broadcast Action: hook for permforming cleanup after a system update. + * + * The broadcast is sent when the system is booting, before the + * BOOT_COMPLETED broadcast. It is only sent to receivers in the system + * image. A receiver for this should do its work and then disable itself + * so that it does not get run again at the next boot. + * @hide + */ + public static final String ACTION_PRE_BOOT_COMPLETED = + "android.intent.action.PRE_BOOT_COMPLETED"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -2251,6 +2262,13 @@ public class Intent implements Parcelable { * @hide */ public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; + /** + * Set when this broadcast is for a boot upgrade, a special mode that + * allows the broadcast to be sent before the system is ready and launches + * the app process with no providers running in it. + * @hide + */ + public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x10000000; // --------------------------------------------------------------------- // --------------------------------------------------------------------- diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index a4b0685bb979..23eb7c17b69b 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -755,6 +755,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String mTopData; boolean mSystemReady = false; boolean mBooting = false; + boolean mWaitingUpdate = false; + boolean mDidUpdate = false; Context mContext; @@ -972,6 +974,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen res.set(0); } } + + ensureBootCompleted(); } break; case SHOW_NOT_RESPONDING_MSG: { synchronized (ActivityManagerService.this) { @@ -992,13 +996,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen proc.anrDialog = d; } - ensureScreenEnabled(); + ensureBootCompleted(); } break; case SHOW_FACTORY_ERROR_MSG: { Dialog d = new FactoryErrorDialog( mContext, msg.getData().getCharSequence("msg")); d.show(); - enableScreenAfterBoot(); + ensureBootCompleted(); } break; case UPDATE_CONFIGURATION_MSG: { final ContentResolver resolver = mContext.getContentResolver(); @@ -1843,12 +1847,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } startProcessLocked(r.processName, r.info.applicationInfo, true, 0, - "activity", r.intent.getComponent()); + "activity", r.intent.getComponent(), false); } private final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, - String hostingType, ComponentName hostingName) { + String hostingType, ComponentName hostingName, boolean allowWhileBooting) { ProcessRecord app = getProcessRecordLocked(processName, info.uid); // We don't have to do anything more if: // (1) There is an existing application record; and @@ -1901,7 +1905,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the system is not ready yet, then hold off on starting this // process until it is. if (!mSystemReady - && (info.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { + && !isAllowedWhileBooting(info) + && !allowWhileBooting) { if (!mProcessesOnHold.contains(app)) { mProcessesOnHold.add(app); } @@ -1912,6 +1917,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return (app.pid != 0) ? app : null; } + boolean isAllowedWhileBooting(ApplicationInfo ai) { + return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0; + } + private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) { if (app.pid > 0 && app.pid != MY_PID) { @@ -5085,8 +5094,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); - List providers = generateApplicationProvidersLocked(app); + boolean normalMode = mSystemReady || isAllowedWhileBooting(app.info); + List providers = normalMode ? generateApplicationProvidersLocked(app) : null; + if (!normalMode) { + Log.i(TAG, "Launching preboot mode app: " + app); + } + if (localLOGV) Log.v( TAG, "New app record " + app + " thread=" + thread.asBinder() + " pid=" + pid); @@ -5102,12 +5116,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWaitForDebugger = mOrigWaitForDebugger; } } + // If the app is being launched for restore or full backup, set it up specially boolean isRestrictedBackupMode = false; if (mBackupTarget != null && mBackupAppName.equals(processName)) { isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE) || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL); } + ensurePackageDexOpt(app.instrumentationInfo != null ? app.instrumentationInfo.packageName : app.info.packageName); @@ -5118,7 +5134,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ? app.instrumentationInfo : app.info, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, - isRestrictedBackupMode, mConfiguration, getCommonServicesLocked()); + isRestrictedBackupMode || !normalMode, + mConfiguration, getCommonServicesLocked()); updateLRUListLocked(app, false); app.lastRequestedGc = SystemClock.uptimeMillis(); } catch (Exception e) { @@ -5284,6 +5301,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } void enableScreenAfterBoot() { + EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN, + SystemClock.uptimeMillis()); mWindowManager.enableScreenAfterBoot(); } @@ -5394,26 +5413,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (booting) { - // Ensure that any processes we had put on hold are now started - // up. - final int NP = mProcessesOnHold.size(); - if (NP > 0) { - ArrayList<ProcessRecord> procs = - new ArrayList<ProcessRecord>(mProcessesOnHold); - for (int ip=0; ip<NP; ip++) { - this.startProcessLocked(procs.get(ip), "on-hold", null); - } - } - if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { - // Tell anyone interested that we are done booting! - synchronized (this) { - broadcastIntentLocked(null, null, - new Intent(Intent.ACTION_BOOT_COMPLETED, null), - null, null, 0, null, null, - android.Manifest.permission.RECEIVE_BOOT_COMPLETED, - false, false, MY_PID, Process.SYSTEM_UID); - } - } + finishBooting(); } trimApplications(); @@ -5421,22 +5421,48 @@ public final class ActivityManagerService extends ActivityManagerNative implemen //mWindowManager.dump(); if (enableScreen) { - EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN, - SystemClock.uptimeMillis()); enableScreenAfterBoot(); } } - final void ensureScreenEnabled() { + final void finishBooting() { + // Ensure that any processes we had put on hold are now started + // up. + final int NP = mProcessesOnHold.size(); + if (NP > 0) { + ArrayList<ProcessRecord> procs = + new ArrayList<ProcessRecord>(mProcessesOnHold); + for (int ip=0; ip<NP; ip++) { + this.startProcessLocked(procs.get(ip), "on-hold", null); + } + } + if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + // Tell anyone interested that we are done booting! + synchronized (this) { + broadcastIntentLocked(null, null, + new Intent(Intent.ACTION_BOOT_COMPLETED, null), + null, null, 0, null, null, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + false, false, MY_PID, Process.SYSTEM_UID); + } + } + } + + final void ensureBootCompleted() { + boolean booting; boolean enableScreen; synchronized (this) { + booting = mBooting; + mBooting = false; enableScreen = !mBooted; mBooted = true; } + + if (booting) { + finishBooting(); + } if (enableScreen) { - EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN, - SystemClock.uptimeMillis()); enableScreenAfterBoot(); } } @@ -5588,6 +5614,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen throw new IllegalArgumentException("File descriptors passed in Intent"); } + if (type == INTENT_SENDER_BROADCAST) { + if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { + throw new IllegalArgumentException( + "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); + } + } + synchronized(this) { int callingUid = Binder.getCallingUid(); try { @@ -7398,7 +7431,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ProcessRecord proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, - cpi.name)); + cpi.name), false); if (proc == null) { Log.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" @@ -8125,17 +8158,93 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mSystemReady) { return; } + + // Check to see if there are any update receivers to run. + if (!mDidUpdate) { + if (mWaitingUpdate) { + return; + } + Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); + List<ResolveInfo> ris = null; + try { + ris = ActivityThread.getPackageManager().queryIntentReceivers( + intent, null, 0); + } catch (RemoteException e) { + } + if (ris != null) { + for (int i=ris.size()-1; i>=0; i--) { + if ((ris.get(i).activityInfo.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) == 0) { + ris.remove(i); + } + } + intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE); + for (int i=0; i<ris.size(); i++) { + ActivityInfo ai = ris.get(i).activityInfo; + intent.setComponent(new ComponentName(ai.packageName, ai.name)); + IIntentReceiver finisher = null; + if (i == 0) { + finisher = new IIntentReceiver.Stub() { + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered) + throws RemoteException { + synchronized (ActivityManagerService.this) { + mDidUpdate = true; + } + systemReady(); + } + }; + } + Log.i(TAG, "Sending system update to: " + intent.getComponent()); + broadcastIntentLocked(null, null, intent, null, finisher, + 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID); + if (i == 0) { + mWaitingUpdate = true; + } + } + } + if (mWaitingUpdate) { + return; + } + mDidUpdate = true; + } + mSystemReady = true; if (!mStartRunning) { return; } } + ArrayList<ProcessRecord> procsToKill = null; + synchronized(mPidsSelfLocked) { + for (int i=mPidsSelfLocked.size()-1; i>=0; i--) { + ProcessRecord proc = mPidsSelfLocked.valueAt(i); + if (!isAllowedWhileBooting(proc.info)){ + if (procsToKill == null) { + procsToKill = new ArrayList<ProcessRecord>(); + } + procsToKill.add(proc); + } + } + } + + if (procsToKill != null) { + synchronized(this) { + for (int i=procsToKill.size()-1; i>=0; i--) { + ProcessRecord proc = procsToKill.get(i); + Log.i(TAG, "Removing system update proc: " + proc); + removeProcessLocked(proc, true); + } + } + } + if (Config.LOGD) Log.d(TAG, "Start running!"); EventLog.writeEvent(LOG_BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis()); synchronized(this) { + // Make sure we have no pre-ready processes sitting around. + if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) { ResolveInfo ri = mContext.getPackageManager() .resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST), @@ -8193,6 +8302,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + // Start up initial activity. + mBooting = true; + try { if (ActivityThread.getPackageManager().hasSystemUidErrors()) { Message msg = Message.obtain(); @@ -8202,8 +8314,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } - // Start up initial activity. - mBooting = true; resumeTopActivityLocked(null); } } @@ -10128,7 +10238,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Not running -- get it started, and enqueue this service record // to be executed when the app comes up. if (startProcessLocked(appName, r.appInfo, true, intentFlags, - "service", r.name) == null) { + "service", r.name, false) == null) { Log.w(TAG, "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " @@ -10930,7 +11040,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName); // startProcessLocked() returns existing proc's record if it's already running ProcessRecord proc = startProcessLocked(app.processName, app, - false, 0, "backup", hostingName); + false, 0, "backup", hostingName, false); if (proc == null) { Log.e(TAG, "Unable to start backup agent process " + r); return false; @@ -11467,10 +11577,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { + int flags = intent.getFlags(); + if (!mSystemReady) { // if the caller really truly claims to know what they're doing, go // ahead and allow the broadcast without launching any receivers - int flags = intent.getFlags(); if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) { intent = new Intent(intent); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -11481,6 +11592,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { + throw new IllegalArgumentException( + "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); + } + final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); @@ -12075,12 +12191,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // restart the application. } - // Not running -- get it started, and enqueue this history record - // to be executed when the app comes up. + // Not running -- get it started, to be executed when the app comes up. if ((r.curApp=startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, - "broadcast", r.curComponent)) == null) { + "broadcast", r.curComponent, + (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0)) + == null) { // Ah, this recipient is unavailable. Finish it if necessary, // and mark the broadcast record as ready for the next. Log.w(TAG, "Unable to launch app " |