Merge "Implement issue #3426299: Introduce application "stopped" state"
diff --git a/api/current.xml b/api/current.xml
index 07d04ca..872e627 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -53230,6 +53230,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"
@@ -53274,6 +53285,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"
@@ -54549,6 +54571,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"
@@ -54582,6 +54615,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"
@@ -58397,6 +58441,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 @@
@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 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 @@
* 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 @@
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 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 @@
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 @@
// 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 @@
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 @@
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 @@
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.Printer;
import android.util.Config;
-import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -326,6 +325,15 @@
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 @@
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 @@
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.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.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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
}
+ 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 @@
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 @@
}
@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 @@
}
@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 @@
};
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
permissionsFixed = base.permissionsFixed;
haveGids = base.haveGids;
+ stopped = base.stopped;
+ notLaunched = base.notLaunched;
disabledComponents = (HashSet<String>) base.disabledComponents.clone();
@@ -7941,6 +8040,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
// 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 @@
|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 @@
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 @@
} 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 @@
}
mPendingPackages.clear();
+ readStoppedLP();
+
mReadMessages.append("Read completed successfully: "
+ mPackages.size() + " packages, "
+ mSharedUsers.size() + " shared uids\n");
@@ -9970,7 +10268,7 @@
}
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 @@
return;
}
forceStopPackageLocked(packageName, pkgUid);
+ try {
+ pm.setPackageStoppedState(packageName, true);
+ } catch (RemoteException e) {
+ }
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -5544,20 +5548,31 @@
// 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;
+
+ 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);
}
- cpr.launchingApp = proc;
- mLaunchingProviders.add(cpr);
- Binder.restoreCallingIdentity(origId);
}
// Make sure the provider is published (the same provider class
@@ -5814,6 +5829,13 @@
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 @@
// 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 @@
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 @@
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 @@
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 5b44d390..3a613bb 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1293,6 +1293,14 @@
}
}
+ // 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.