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
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="&quot;android.intent.action.MY_PACKAGE_REPLACED&quot;"
+ 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="&quot;android.intent.action.PACKAGE_FIRST_LAUNCH&quot;"
+ 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 @@
     @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 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 @@
             }
         }
 
+        // 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.