diff options
author | Dianne Hackborn <hackbod@google.com> | 2011-02-24 14:40:20 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2011-02-25 14:46:27 -0800 |
commit | e7f972122db87dc54e41ed1a6e417534d43bca3a (patch) | |
tree | 8342973439086cceaa3c66fed400bf894abf36de | |
parent | 56d9353195e4c767e60ecfab1e118d3cc30f3a91 (diff) | |
download | frameworks_base-e7f972122db87dc54e41ed1a6e417534d43bca3a.zip frameworks_base-e7f972122db87dc54e41ed1a6e417534d43bca3a.tar.gz frameworks_base-e7f972122db87dc54e41ed1a6e417534d43bca3a.tar.bz2 |
Implement issue #3426299: Introduce application "stopped" state
The package manager now keeps track of whether an application is
stopped. There are new intent flags to control whether intent
filters in a stopped application will match the intent. This is
currently used in one place, sending broadcasts, so that stopped
apps can not be launched due to background processes.
The package manager during first init makes sure no applications
are in the stopped state. When new applications are installed,
that begin in the stopped state. When the activity manager is
launching a component of an application, it ensures the application
is taken out of the stopped state.
The "force stop" button in manage applications will now put an
application back in to the stopped state; it can't go back out
of the stopped state until one of its components is launched by
the activity manager.
There will probably be a few more places where we need to filter
stopped applications out of intent matches, but doing this for
broadcast is a very big first step.
This also introduces a new broadcast that is sent to an application
after it is replaced with a new .apk. But only if the app is not
in the stopped state. This makes it a lot easier for developers to
implement code to get their application back in proper running shape
after an upgrade.
Finally another new broadcast is added that is sent to a package's
installer at the first time it is launched. This allows the installer
to tell the package about it being installed only when it is first
actually used.
Change-Id: I589c53ff0e0ece868fe734ace4439c0d202dca2d
-rw-r--r-- | api/current.xml | 55 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 42 | ||||
-rw-r--r-- | core/java/android/content/pm/ApplicationInfo.java | 6 | ||||
-rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 6 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 13 | ||||
-rw-r--r-- | core/java/android/server/BluetoothInputProfileHandler.java | 1 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 2 | ||||
-rw-r--r-- | services/java/com/android/server/IntentResolver.java | 19 | ||||
-rw-r--r-- | services/java/com/android/server/PackageManagerService.java | 342 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 74 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityStack.java | 8 |
11 files changed, 531 insertions, 37 deletions
diff --git a/api/current.xml b/api/current.xml index 1089604..a850434 100644 --- a/api/current.xml +++ b/api/current.xml @@ -53165,6 +53165,17 @@ visibility="public" > </field> +<field name="ACTION_MY_PACKAGE_REPLACED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.MY_PACKAGE_REPLACED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_NEW_OUTGOING_CALL" type="java.lang.String" transient="false" @@ -53209,6 +53220,17 @@ visibility="public" > </field> +<field name="ACTION_PACKAGE_FIRST_LAUNCH" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.PACKAGE_FIRST_LAUNCH"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_PACKAGE_INSTALL" type="java.lang.String" transient="false" @@ -54484,6 +54506,17 @@ visibility="public" > </field> +<field name="FLAG_EXCLUDE_STOPPED_PACKAGES" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_FROM_BACKGROUND" type="int" transient="false" @@ -54517,6 +54550,17 @@ visibility="public" > </field> +<field name="FLAG_INCLUDE_STOPPED_PACKAGES" + type="int" + transient="false" + volatile="false" + value="32" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_RECEIVER_REGISTERED_ONLY" type="int" transient="false" @@ -58332,6 +58376,17 @@ visibility="public" > </field> +<field name="FLAG_STOPPED" + type="int" + transient="false" + volatile="false" + value="2097152" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_SUPPORTS_LARGE_SCREENS" type="int" transient="false" diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ce7f096..7bdd1b9 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1312,6 +1312,17 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED"; /** + * Broadcast Action: A new version of your application has been installed + * over an existing one. This is only sent to the application that was + * replaced. It does not contain any additional data; to receive it, just + * use an intent filter for this action. + * + * <p class="note">This is a protected intent that can only be sent + * by the system. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED"; + /** * Broadcast Action: An existing application package has been removed from * the device. The data contains the name of the package. The package * that is being installed does <em>not</em> receive this Intent. @@ -1403,6 +1414,17 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED"; /** + * Broadcast Action: Sent to the installer package of an application + * when that application is first launched (that is the first time it + * is moved out of the stopped state). The data contains the name of the package. + * + * <p class="note">This is a protected intent that can only be sent + * by the system. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; + + /** * Broadcast Action: Resources for a set of packages (which were * previously unavailable) are currently * available since the media on which they exist is available. @@ -2442,6 +2464,20 @@ public class Intent implements Parcelable, Cloneable { * been found to create the final resolved list. */ public static final int FLAG_DEBUG_LOG_RESOLUTION = 0x00000008; + /** + * If set, this intent will not match any components in packages that + * are currently stopped. If this is not set, then the default behavior + * is to include such applications in the result. + */ + public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010; + /** + * If set, this intent will always match any components in packages that + * are currently stopped. This is the default behavior when + * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set. If both of these + * flags are set, this one wins (it allows overriding of exclude for + * places where the framework may automatically set the exclude flag). + */ + public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020; /** * If set, the new activity is not kept in the history stack. As soon as @@ -3915,6 +3951,12 @@ public class Intent implements Parcelable, Cloneable { return mFlags; } + /** @hide */ + public boolean isExcludingStopped() { + return (mFlags&(FLAG_EXCLUDE_STOPPED_PACKAGES|FLAG_INCLUDE_STOPPED_PACKAGES)) + == FLAG_EXCLUDE_STOPPED_PACKAGES; + } + /** * Retrieve the application package name this Intent is limited to. When * resolving an Intent, if non-null this limits the resolution to only diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2d95781..92b2c3b 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -278,6 +278,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_LARGE_HEAP = 1<<20; /** + * Value for {@link #flags}: true if this application's package is in + * the stopped state. + */ + public static final int FLAG_STOPPED = 1<<21; + + /** * Value for {@link #flags}: Set to true if the application has been * installed using the forward lock option. * diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 034525e..fbf8f92 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -210,6 +210,12 @@ interface IPackageManager { int getApplicationEnabledSetting(in String packageName); /** + * Set whether the given package should be considered stopped, making + * it not visible to implicit intents that filter out stopped packages. + */ + void setPackageStoppedState(String packageName, boolean stopped); + + /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage * on the device is greater than or equal to the requested diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 7676258..7ebfda4 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2817,6 +2817,9 @@ public class PackageParser { // User set enabled state. public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + // Whether the package has been stopped. + public boolean mSetStopped = false; + // Additional data supplied by callers. public Object mExtras; @@ -3071,6 +3074,11 @@ public class PackageParser { if (!sCompatibilityModeEnabled) { p.applicationInfo.disableCompatibilityMode(); } + if (p.mSetStopped) { + p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED; + } else { + p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED; + } return p.applicationInfo; } @@ -3085,6 +3093,11 @@ public class PackageParser { if (!sCompatibilityModeEnabled) { ai.disableCompatibilityMode(); } + if (p.mSetStopped) { + p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED; + } else { + p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED; + } if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { ai.enabled = true; } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java index cdc0f2d..e6513fd 100644 --- a/core/java/android/server/BluetoothInputProfileHandler.java +++ b/core/java/android/server/BluetoothInputProfileHandler.java @@ -187,6 +187,7 @@ final class BluetoothInputProfileHandler { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothInputDevice.EXTRA_STATE, state); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 12d7afd..beb824c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -35,10 +35,12 @@ <protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" /> <protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" /> + <protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" /> <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" /> + <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" /> <protected-broadcast android:name="android.intent.action.UID_REMOVED" /> <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" /> <protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" /> diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index e9ee12c..b78389b 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -36,7 +36,6 @@ import android.util.LogPrinter; import android.util.Printer; import android.util.Config; -import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; @@ -326,6 +325,15 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return true; } + /** + * Returns whether the object associated with the given filter is + * "stopped," that is whether it should not be included in the result + * if the intent requests to excluded stopped objects. + */ + protected boolean isFilterStopped(F filter) { + return false; + } + protected String packageForFilter(F filter) { return null; } @@ -496,6 +504,8 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { final String action = intent.getAction(); final Uri data = intent.getData(); + final boolean excludingStopped = intent.isExcludingStopped(); + final int N = src != null ? src.size() : 0; boolean hasNonDefaults = false; int i; @@ -504,6 +514,13 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { int match; if (debug) Slog.v(TAG, "Matching against filter " + filter); + if (excludingStopped && isFilterStopped(filter)) { + if (debug) { + Slog.v(TAG, " Filter's target is stopped; skipping"); + } + continue; + } + // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 7af60c5..f1a6e15 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -85,7 +85,6 @@ import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.provider.Settings; import android.security.SystemKeyStore; import android.util.*; import android.view.Display; @@ -114,12 +113,10 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; -import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; @@ -140,6 +137,7 @@ class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_PREFERRED = false; private static final boolean DEBUG_UPGRADE = false; private static final boolean DEBUG_INSTALL = false; + private static final boolean DEBUG_STOPPED = false; private static final boolean MULTIPLE_APPLICATION_UIDS = true; private static final int RADIO_UID = Process.PHONE_UID; @@ -153,8 +151,6 @@ class PackageManagerService extends IPackageManager.Stub { private static final boolean GET_CERTIFICATES = true; - private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled"; - private static final int REMOVE_EVENTS = FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM; private static final int ADD_EVENTS = @@ -360,6 +356,7 @@ class PackageManagerService extends IPackageManager.Stub { static final int MCS_GIVE_UP = 11; static final int UPDATED_MEDIA_STATUS = 12; static final int WRITE_SETTINGS = 13; + static final int WRITE_STOPPED_PACKAGES = 14; static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds @@ -603,11 +600,14 @@ class PackageManagerService extends IPackageManager.Stub { } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, - extras, null); + extras, null, null); if (update) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, res.pkg.applicationInfo.packageName, - extras, null); + extras, null, null); + sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, + null, null, + res.pkg.applicationInfo.packageName, null); } if (res.removedInfo.args != null) { // Remove the replaced package's older resources safely now @@ -661,10 +661,19 @@ class PackageManagerService extends IPackageManager.Stub { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { removeMessages(WRITE_SETTINGS); + removeMessages(WRITE_STOPPED_PACKAGES); mSettings.writeLP(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; + case WRITE_STOPPED_PACKAGES: { + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + synchronized (mPackages) { + removeMessages(WRITE_STOPPED_PACKAGES); + mSettings.writeStoppedLP(); + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + } break; } } } @@ -675,6 +684,12 @@ class PackageManagerService extends IPackageManager.Stub { } } + void scheduleWriteStoppedPackagesLocked() { + if (!mHandler.hasMessages(WRITE_STOPPED_PACKAGES)) { + mHandler.sendEmptyMessageDelayed(WRITE_STOPPED_PACKAGES, WRITE_SETTINGS_DELAY); + } + } + static boolean installOnSd(int flags) { if (((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) || ((flags & PackageManager.INSTALL_INTERNAL) != 0)) { @@ -1489,6 +1504,7 @@ class PackageManagerService extends IPackageManager.Stub { ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath(); ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; ps.pkg.mSetEnabled = ps.enabled; + ps.pkg.mSetStopped = ps.stopped; } return generatePackageInfo(ps.pkg, flags); } @@ -4096,6 +4112,18 @@ class PackageManagerService extends IPackageManager.Stub { } @Override + protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter) { + PackageParser.Package p = filter.activity.owner; + if (p != null) { + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps != null) { + return ps.stopped; + } + } + return false; + } + + @Override protected String packageForFilter(PackageParser.ActivityIntentInfo info) { return info.activity.owner.packageName; } @@ -4253,6 +4281,18 @@ class PackageManagerService extends IPackageManager.Stub { } @Override + protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter) { + PackageParser.Package p = filter.service.owner; + if (p != null) { + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps != null) { + return ps.stopped; + } + } + return false; + } + + @Override protected String packageForFilter(PackageParser.ServiceIntentInfo info) { return info.service.owner.packageName; } @@ -4355,7 +4395,7 @@ class PackageManagerService extends IPackageManager.Stub { }; private static final void sendPackageBroadcast(String action, String pkg, - Bundle extras, IIntentReceiver finishedReceiver) { + Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) { IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try { @@ -4364,6 +4404,9 @@ class PackageManagerService extends IPackageManager.Stub { if (extras != null) { intent.putExtras(extras); } + if (targetPkg != null) { + intent.setPackage(targetPkg); + } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); am.broadcastIntent(null, intent, null, finishedReceiver, 0, null, null, null, finishedReceiver != null, false); @@ -4499,13 +4542,13 @@ class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, removedUid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, - extras, null); + extras, null, null); } if (addedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, addedUid); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, - extras, null); + extras, null, null); } } @@ -6218,8 +6261,12 @@ class PackageManagerService extends IPackageManager.Stub { extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid); extras.putBoolean(Intent.EXTRA_REPLACING, true); - sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null); - sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, + extras, null, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, + extras, null, null); + sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, + null, packageName, null); } } // Force a gc here. @@ -6250,10 +6297,11 @@ class PackageManagerService extends IPackageManager.Stub { extras.putBoolean(Intent.EXTRA_REPLACING, true); } if (removedPackage != null) { - sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, + extras, null, null); } if (removedUid >= 0) { - sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null); + sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null); } } } @@ -6950,7 +6998,45 @@ class PackageManagerService extends IPackageManager.Stub { extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); extras.putInt(Intent.EXTRA_UID, packageUid); - sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null); + sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null); + } + + public void setPackageStoppedState(String packageName, boolean stopped) { + PackageSetting pkgSetting; + final int uid = Binder.getCallingUid(); + final int permission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); + final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (!allowedByPermission && (uid != pkgSetting.userId)) { + throw new SecurityException( + "Permission Denial: attempt to change stopped state from pid=" + + Binder.getCallingPid() + + ", uid=" + uid + ", package uid=" + pkgSetting.userId); + } + if (DEBUG_STOPPED && stopped) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Stopping package " + packageName, e); + } + if (pkgSetting.stopped != stopped) { + pkgSetting.stopped = stopped; + pkgSetting.pkg.mSetStopped = stopped; + if (pkgSetting.notLaunched) { + if (pkgSetting.installerPackageName != null) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, + pkgSetting.installerPackageName, null, + pkgSetting.name, null); + } + pkgSetting.notLaunched = false; + } + scheduleWriteStoppedPackagesLocked(); + } + } } public String getInstallerPackageName(String packageName) { @@ -7300,11 +7386,15 @@ class PackageManagerService extends IPackageManager.Stub { date.setTime(ps.firstInstallTime); pw.println(sdf.format(date)); pw.print(" lastUpdateTime="); date.setTime(ps.lastUpdateTime); pw.println(sdf.format(date)); + if (ps.installerPackageName != null) { + pw.print(" installerPackageName="); pw.println(ps.installerPackageName); + } pw.print(" signatures="); pw.println(ps.signatures); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); pw.print(" haveGids="); pw.println(ps.haveGids); pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags)); pw.print(" installStatus="); pw.print(ps.installStatus); + pw.print(" stopped="); pw.print(ps.stopped); pw.print(" enabled="); pw.println(ps.enabled); if (ps.disabledComponents.size() > 0) { pw.println(" disabledComponents:"); @@ -7841,6 +7931,13 @@ class PackageManagerService extends IPackageManager.Stub { boolean permissionsFixed; boolean haveGids; + // Whether this package is currently stopped, thus can not be + // started until explicitly launched by the user. + public boolean stopped; + + // Set to true if we have never launched this app. + public boolean notLaunched; + /* Explicitly disabled components */ HashSet<String> disabledComponents = new HashSet<String>(0); /* Explicitly enabled components */ @@ -7885,6 +7982,8 @@ class PackageManagerService extends IPackageManager.Stub { permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; + stopped = base.stopped; + notLaunched = base.notLaunched; disabledComponents = (HashSet<String>) base.disabledComponents.clone(); @@ -7941,6 +8040,8 @@ class PackageManagerService extends IPackageManager.Stub { signatures = base.signatures; permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; + stopped = base.stopped; + notLaunched = base.notLaunched; disabledComponents = base.disabledComponents; enabledComponents = base.enabledComponents; enabled = base.enabled; @@ -8039,6 +8140,8 @@ class PackageManagerService extends IPackageManager.Stub { private final File mSettingsFilename; private final File mBackupSettingsFilename; private final File mPackageListFilename; + private final File mStoppedPackagesFilename; + private final File mBackupStoppedPackagesFilename; private final HashMap<String, PackageSetting> mPackages = new HashMap<String, PackageSetting>(); // List of replaced system applications @@ -8138,6 +8241,8 @@ class PackageManagerService extends IPackageManager.Stub { mSettingsFilename = new File(systemDir, "packages.xml"); mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); mPackageListFilename = new File(systemDir, "packages.list"); + mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml"); + mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml"); } PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage, @@ -8393,6 +8498,16 @@ class PackageManagerService extends IPackageManager.Stub { nativeLibraryPathString, vc, pkgFlags); p.setTimeStamp(codePath.lastModified()); p.sharedUser = sharedUser; + // If this is not a system app, it starts out stopped. + if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + if (DEBUG_STOPPED) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(TAG, "Stopping package " + name, e); + } + p.stopped = true; + p.notLaunched = true; + } if (sharedUser != null) { p.userId = sharedUser.userId; } else if (MULTIPLE_APPLICATION_UIDS) { @@ -8439,6 +8554,7 @@ class PackageManagerService extends IPackageManager.Stub { private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) { p.pkg = pkg; pkg.mSetEnabled = p.enabled; + pkg.mSetStopped = p.stopped; final String codePath = pkg.applicationInfo.sourceDir; final String resourcePath = pkg.applicationInfo.publicSourceDir; // Update code path if needed @@ -8655,6 +8771,180 @@ class PackageManagerService extends IPackageManager.Stub { } } + void writeStoppedLP() { + // Keep the old stopped packages around until we know the new ones have + // been successfully written. + if (mStoppedPackagesFilename.exists()) { + // Presence of backup settings file indicates that we failed + // to persist packages earlier. So preserve the older + // backup for future reference since the current packages + // might have been corrupted. + if (!mBackupStoppedPackagesFilename.exists()) { + if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) { + Log.wtf(TAG, "Unable to backup package manager stopped packages, " + + "current changes will be lost at reboot"); + return; + } + } else { + mStoppedPackagesFilename.delete(); + Slog.w(TAG, "Preserving older stopped packages backup"); + } + } + + try { + FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename); + BufferedOutputStream str = new BufferedOutputStream(fstr); + + //XmlSerializer serializer = XmlUtils.serializerInstance(); + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(str, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, "stopped-packages"); + + for (PackageSetting pkg : mPackages.values()) { + if (pkg.stopped) { + serializer.startTag(null, "pkg"); + serializer.attribute(null, "name", pkg.name); + if (pkg.notLaunched) { + serializer.attribute(null, "nl", "1"); + } + serializer.endTag(null, "pkg"); + } + } + + serializer.endTag(null, "stopped-packages"); + + serializer.endDocument(); + + str.flush(); + FileUtils.sync(fstr); + str.close(); + + // New settings successfully written, old ones are no longer + // needed. + mBackupStoppedPackagesFilename.delete(); + FileUtils.setPermissions(mStoppedPackagesFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + + // Done, all is good! + return; + + } catch(java.io.IOException e) { + Log.wtf(TAG, "Unable to write package manager stopped packages, " + + " current changes will be lost at reboot", e); + } + + // Clean up partially written files + if (mStoppedPackagesFilename.exists()) { + if (!mStoppedPackagesFilename.delete()) { + Log.i(TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename); + } + } + } + + // Note: assumed "stopped" field is already cleared in all packages. + void readStoppedLP() { + FileInputStream str = null; + if (mBackupStoppedPackagesFilename.exists()) { + try { + str = new FileInputStream(mBackupStoppedPackagesFilename); + mReadMessages.append("Reading from backup stopped packages file\n"); + reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file"); + if (mSettingsFilename.exists()) { + // If both the backup and normal file exist, we + // ignore the normal one since it might have been + // corrupted. + Slog.w(TAG, "Cleaning up stopped packages file " + + mStoppedPackagesFilename); + mStoppedPackagesFilename.delete(); + } + } catch (java.io.IOException e) { + // We'll try for the normal settings file. + } + } + + try { + if (str == null) { + if (!mStoppedPackagesFilename.exists()) { + mReadMessages.append("No stopped packages file found\n"); + reportSettingsProblem(Log.INFO, "No stopped packages file file; " + + "assuming all started"); + // At first boot, make sure no packages are stopped. + // We usually want to have third party apps initialize + // in the stopped state, but not at first boot. + for (PackageSetting pkg : mPackages.values()) { + pkg.stopped = false; + pkg.notLaunched = false; + } + return; + } + str = new FileInputStream(mStoppedPackagesFilename); + } + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in stopped packages file\n"); + reportSettingsProblem(Log.WARN, + "No start tag found in package manager stopped packages"); + return; + } + + int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("pkg")) { + String name = parser.getAttributeValue(null, "name"); + PackageSetting ps = mPackages.get(name); + if (ps != null) { + ps.stopped = true; + if ("1".equals(parser.getAttributeValue(null, "nl"))) { + ps.notLaunched = true; + } + } else { + Slog.w(TAG, "No package known for stopped package: " + name); + } + XmlUtils.skipCurrentTag(parser); + } else { + Slog.w(TAG, "Unknown element under <stopped-packages>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + + } catch(XmlPullParserException e) { + mReadMessages.append("Error reading: " + e.toString()); + reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e); + Log.wtf(TAG, "Error reading package manager stopped packages", e); + + } catch(java.io.IOException e) { + mReadMessages.append("Error reading: " + e.toString()); + reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); + Log.wtf(TAG, "Error reading package manager stopped packages", e); + + } + } + void writeLP() { //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); @@ -8667,7 +8957,8 @@ class PackageManagerService extends IPackageManager.Stub { // might have been corrupted. if (!mBackupSettingsFilename.exists()) { if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { - Slog.w(TAG, "Unable to backup package manager settings, current changes will be lost at reboot"); + Log.wtf(TAG, "Unable to backup package manager settings, " + + " current changes will be lost at reboot"); return; } } else { @@ -8829,17 +9120,21 @@ class PackageManagerService extends IPackageManager.Stub { |FileUtils.S_IROTH, -1, -1); + writeStoppedLP(); + return; } catch(XmlPullParserException e) { - Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + Log.wtf(TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); } catch(java.io.IOException e) { - Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + Log.wtf(TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); } // Clean up partially written files if (mSettingsFilename.exists()) { if (!mSettingsFilename.delete()) { - Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename); + Log.wtf(TAG, "Failed to clean up mangled file: " + mSettingsFilename); } } //Debug.stopMethodTracing(); @@ -9063,6 +9358,7 @@ class PackageManagerService extends IPackageManager.Stub { if (type != XmlPullParser.START_TAG) { mReadMessages.append("No start tag found in settings file\n"); reportSettingsProblem(Log.WARN, "No start tag found in package manager settings"); + Log.wtf(TAG, "No start tag found in package manager settings"); return false; } @@ -9126,12 +9422,12 @@ class PackageManagerService extends IPackageManager.Stub { } catch(XmlPullParserException e) { mReadMessages.append("Error reading: " + e.toString()); reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Slog.e(TAG, "Error reading package manager settings", e); + Log.wtf(TAG, "Error reading package manager settings", e); } catch(java.io.IOException e) { mReadMessages.append("Error reading: " + e.toString()); reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); - Slog.e(TAG, "Error reading package manager settings", e); + Log.wtf(TAG, "Error reading package manager settings", e); } @@ -9165,6 +9461,8 @@ class PackageManagerService extends IPackageManager.Stub { } mPendingPackages.clear(); + readStoppedLP(); + mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " + mSharedUsers.size() + " shared uids\n"); @@ -9970,7 +10268,7 @@ class PackageManagerService extends IPackageManager.Stub { } String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; - sendPackageBroadcast(action, null, extras, finishedReceiver); + sendPackageBroadcast(action, null, extras, null, finishedReceiver); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 399c19a..8d30868 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -3145,6 +3145,10 @@ public final class ActivityManagerService extends ActivityManagerNative return; } forceStopPackageLocked(packageName, pkgUid); + try { + pm.setPackageStoppedState(packageName, true); + } catch (RemoteException e) { + } } } finally { Binder.restoreCallingIdentity(callingId); @@ -5544,20 +5548,31 @@ public final class ActivityManagerService extends ActivityManagerNative // started. if (i >= N) { final long origId = Binder.clearCallingIdentity(); - ProcessRecord proc = startProcessLocked(cpi.processName, - cpr.appInfo, false, 0, "content provider", - new ComponentName(cpi.applicationInfo.packageName, - cpi.name), false); - if (proc == null) { - Slog.w(TAG, "Unable to launch app " - + cpi.applicationInfo.packageName + "/" - + cpi.applicationInfo.uid + " for provider " - + name + ": process is bad"); - return null; - } - cpr.launchingApp = proc; - mLaunchingProviders.add(cpr); - Binder.restoreCallingIdentity(origId); + + try { + // Content provider is now in use, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + cpr.appInfo.packageName, false); + } catch (RemoteException e) { + } + + ProcessRecord proc = startProcessLocked(cpi.processName, + cpr.appInfo, false, 0, "content provider", + new ComponentName(cpi.applicationInfo.packageName, + cpi.name), false); + if (proc == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": process is bad"); + return null; + } + cpr.launchingApp = proc; + mLaunchingProviders.add(cpr); + } finally { + Binder.restoreCallingIdentity(origId); + } } // Make sure the provider is published (the same provider class @@ -5814,6 +5829,13 @@ public final class ActivityManagerService extends ActivityManagerNative updateLruProcessLocked(app, true, true); } + // This package really, really can not be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + info.packageName, false); + } catch (RemoteException e) { + } + if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) { app.persistent = true; @@ -9354,6 +9376,13 @@ public final class ActivityManagerService extends ActivityManagerNative // restarting state. mRestartingServices.remove(r); + // Service is now being launched, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + r.packageName, false); + } catch (RemoteException e) { + } + final String appName = r.processName; ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid); if (app != null && app.thread != null) { @@ -10248,6 +10277,13 @@ public final class ActivityManagerService extends ActivityManagerNative ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name); } + // Backup agent is now in use, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + app.packageName, false); + } catch (RemoteException e) { + } + BackupRecord r = new BackupRecord(ss, app, backupMode); ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName); // startProcessLocked() returns existing proc's record if it's already running @@ -10535,6 +10571,9 @@ public final class ActivityManagerService extends ActivityManagerNative boolean ordered, boolean sticky, int callingPid, int callingUid) { intent = new Intent(intent); + // By default broadcasts do not go to stopped apps. + intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); + if (DEBUG_BROADCAST_LIGHT) Slog.v( TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent + " ordered=" + ordered); @@ -11566,6 +11605,13 @@ public final class ActivityManagerService extends ActivityManagerNative info.activityInfo.name); r.curReceiver = info.activityInfo; + // Broadcast is being executed, its package can't be stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + r.curComponent.getPackageName(), false); + } catch (RemoteException e) { + } + // Is this receiver's application already running? ProcessRecord app = getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 5b44d39..3a613bb 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1293,6 +1293,14 @@ public class ActivityStack { } } + // Launching this app's activity, make sure the app is no longer + // considered stopped. + try { + AppGlobals.getPackageManager().setPackageStoppedState( + next.packageName, false); + } catch (RemoteException e1) { + } + // We are starting up the next activity, so tell the window manager // that the previous one will be hidden soon. This way it can know // to ignore it when computing the desired screen orientation. |