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="&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"
@@ -53274,6 +53285,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"
@@ -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.