Fix issue #2438980: Implement package watcher for voice recognizer service setting

I am getting tired of writing package monitor code, realized this is missing in
a number of places, and at this point it has gotten complicated enough that I
don't think anyone actually does it 100% right so:

Introducing PackageMonitor.

Yes there are no Java docs.  I am still playing around with just what this
thing is to figure out what makes sense and how people will use it.  It is
being used to fix this bug for monitoring voice recognizers (integrating the
code from the settings provider for setting an initial value), to replace
the existing code for monitoring input methods (and fix the bug where we
wouldn't remove an input method from the enabled list when it got
uninstalled), to now monitor live wallpaper package changes (now allowing
us to avoid reverting back to the default live wallpaper when the current
one is updated!), and to monitor device admin changes.

Also includes a fix so you can't uninstall an .apk that is currently enabled
as a device admin.

Also includes a fix where the default time zone was not initialized early
enough which should fix issue #2455507 (Observed Google services frame work crash).

In addition, this finally introduces a mechanism to determine if the
"force stop" button should be enabled, with convenience in PackageMonitor
for system services to handle it.  All services have been updated to support
this.  There is also new infrastructure for reporting battery usage as an
applicatin error report.
diff --git a/api/current.xml b/api/current.xml
index 33a8020..a753165 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23748,7 +23748,7 @@
 >
 <parameter name="parcel" type="android.os.Parcel">
 </parameter>
-<parameter name="flags" type="int">
+<parameter name="flagz" type="int">
 </parameter>
 </method>
 <field name="CREATOR"
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 832f599..fe81056 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -16,8 +16,16 @@
 
 package android.app;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Printer;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -38,6 +46,13 @@
  */
 
 public class ApplicationErrorReport implements Parcelable {
+    // System property defining error report receiver for system apps
+    static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
+
+    // System property defining default error report receiver
+    static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
+
+    
     /**
      * Uninitialized error report.
      */
@@ -54,8 +69,13 @@
     public static final int TYPE_ANR = 2;
 
     /**
+     * An error report about an application that's consuming too much battery.
+     */
+    public static final int TYPE_BATTERY = 3;
+
+    /**
      * Type of this report. Can be one of {@link #TYPE_NONE},
-     * {@link #TYPE_CRASH} or {@link #TYPE_ANR}.
+     * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}.
      */
     public int type;
 
@@ -99,6 +119,11 @@
     public AnrInfo anrInfo;
 
     /**
+     * Text containing battery usage data.
+     */
+    public String batteryText;
+    
+    /**
      * Create an uninitialized instance of {@link ApplicationErrorReport}.
      */
     public ApplicationErrorReport() {
@@ -112,6 +137,68 @@
         readFromParcel(in);
     }
 
+    public static ComponentName getErrorReportReceiver(Context context,
+            String packageName, int appFlags) {
+        // check if error reporting is enabled in secure settings
+        int enabled = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.SEND_ACTION_APP_ERROR, 0);
+        if (enabled == 0) {
+            return null;
+        }
+
+        PackageManager pm = context.getPackageManager();
+
+        // look for receiver in the installer package
+        String candidate = pm.getInstallerPackageName(packageName);
+        ComponentName result = getErrorReportReceiver(pm, packageName, candidate);
+        if (result != null) {
+            return result;
+        }
+
+        // if the error app is on the system image, look for system apps
+        // error receiver
+        if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+            candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
+            result = getErrorReportReceiver(pm, packageName, candidate);
+            if (result != null) {
+                return result;
+            }
+        }
+
+        // if there is a default receiver, try that
+        candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
+        return getErrorReportReceiver(pm, packageName, candidate);
+    }
+    
+    /**
+     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
+     *
+     * @param pm PackageManager isntance
+     * @param errorPackage package which caused the error
+     * @param receiverPackage candidate package to receive the error
+     * @return activity component within receiverPackage which handles
+     * ACTION_APP_ERROR, or null if not found
+     */
+    static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
+            String receiverPackage) {
+        if (receiverPackage == null || receiverPackage.length() == 0) {
+            return null;
+        }
+
+        // break the loop if it's the error report receiver package that crashed
+        if (receiverPackage.equals(errorPackage)) {
+            return null;
+        }
+
+        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+        intent.setPackage(receiverPackage);
+        ResolveInfo info = pm.resolveActivity(intent, 0);
+        if (info == null || info.activityInfo == null) {
+            return null;
+        }
+        return new ComponentName(receiverPackage, info.activityInfo.name);
+    }
+
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(type);
         dest.writeString(packageName);
@@ -127,6 +214,9 @@
             case TYPE_ANR:
                 anrInfo.writeToParcel(dest, flags);
                 break;
+            case TYPE_BATTERY:
+                dest.writeString(batteryText);
+                break;
         }
     }
 
@@ -142,10 +232,17 @@
             case TYPE_CRASH:
                 crashInfo = new CrashInfo(in);
                 anrInfo = null;
+                batteryText = null;
                 break;
             case TYPE_ANR:
                 anrInfo = new AnrInfo(in);
                 crashInfo = null;
+                batteryText = null;
+                break;
+            case TYPE_BATTERY:
+                batteryText = in.readString();
+                anrInfo = null;
+                crashInfo = null;
                 break;
         }
     }
@@ -347,6 +444,9 @@
             case TYPE_ANR:
                 anrInfo.dump(pw, prefix);
                 break;
+            case TYPE_BATTERY:
+                pw.println(batteryText);
+                break;
         }
     }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1d004ee..c7c9429 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1146,7 +1146,7 @@
     private DevicePolicyManager getDevicePolicyManager() {
         synchronized (mSync) {
             if (mDevicePolicyManager == null) {
-                mDevicePolicyManager = new DevicePolicyManager(this,
+                mDevicePolicyManager = DevicePolicyManager.create(this,
                         mMainThread.getHandler());
             }
         }
diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java
index bedf4b4..61e1bb339 100644
--- a/core/java/android/app/DeviceAdminInfo.java
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -18,6 +18,7 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -331,6 +332,19 @@
         return res;
     }
     
+    /** @hide */
+    public void writePoliciesToXml(XmlSerializer out)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        out.attribute(null, "flags", Integer.toString(mUsesPolicies));
+    }
+    
+    /** @hide */
+    public void readPoliciesFromXml(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        mUsesPolicies = Integer.parseInt(
+                parser.getAttributeValue(null, "flags"));
+    }
+    
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "Receiver:");
         mReceiver.dump(pw, prefix + "  ");
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
index d611807..0e8c1ab 100644
--- a/core/java/android/app/DevicePolicyManager.java
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -49,13 +49,18 @@
     
     private final Handler mHandler;
 
-    /*package*/ DevicePolicyManager(Context context, Handler handler) {
+    private DevicePolicyManager(Context context, Handler handler) {
         mContext = context;
         mHandler = handler;
         mService = IDevicePolicyManager.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
     }
 
+    /*package*/ static DevicePolicyManager create(Context context, Handler handler) {
+        DevicePolicyManager me = new DevicePolicyManager(context, handler);
+        return me.mService != null ? me : null;
+    }
+    
     /**
      * Activity action: ask the user to add a new device administrator to the system.
      * The desired policy is the ComponentName of the policy in the
@@ -133,6 +138,20 @@
     }
     
     /**
+     * @hide
+     */
+    public boolean packageHasActiveAdmins(String packageName) {
+        if (mService != null) {
+            try {
+                return mService.packageHasActiveAdmins(packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+    
+    /**
      * Remove a current administration component.  This can only be called
      * by the application that owns the administration component; if you
      * try to remove someone else's component, a security exception will be
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
index ae5c4bf..b138720 100644
--- a/core/java/android/app/IDevicePolicyManager.aidl
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -49,6 +49,7 @@
     void setActiveAdmin(in ComponentName policyReceiver);
     boolean isAdminActive(in ComponentName policyReceiver);
     List<ComponentName> getActiveAdmins();
+    boolean packageHasActiveAdmins(String packageName);
     void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
     void removeActiveAdmin(in ComponentName policyReceiver);
     
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index c4ba05d..7ca0f01 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -126,7 +126,7 @@
     }
     
     /**
-     * The samee as {@link #flattenToString()}, but abbreviates the class
+     * The same as {@link #flattenToString()}, but abbreviates the class
      * name if it is a suffix of the package.  The result can still be used
      * with {@link #unflattenFromString(String)}.
      * 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1b0437c..607605d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1316,6 +1316,21 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
     /**
+     * @hide
+     * Broadcast Action: Ask system services if there is any reason to
+     * restart the given package.  The data contains the name of the
+     * package.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+     * <li> {@link #EXTRA_PACKAGES} String array of all packages to check.
+     * </ul>
+     *
+     * <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_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
+    /**
      * Broadcast Action: The user has restarted a package, and all of its
      * processes have been killed.  All runtime state
      * associated with it (processes, alarms, notifications, etc) should
@@ -2098,6 +2113,7 @@
      * number to call in a {@link android.content.Intent#ACTION_CALL}.
      */
     public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+    
     /**
      * Used as an int extra field in {@link android.content.Intent#ACTION_UID_REMOVED}
      * intents to supply the uid the package had been assigned.  Also an optional
@@ -2108,6 +2124,11 @@
     public static final String EXTRA_UID = "android.intent.extra.UID";
 
     /**
+     * @hide String array of package names.
+     */
+    public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
+
+    /**
      * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
      * intents to indicate whether this represents a full uninstall (removing
      * both the code and its data) or a partial uninstall (leaving its data,
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b706c5c..56a05ee 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -750,11 +750,8 @@
      * Checkin server version of dump to produce more compact, computer-readable log.
      * 
      * NOTE: all times are expressed in 'ms'.
-     * @param fd
-     * @param pw
-     * @param which
      */
-    private final void dumpCheckinLocked(PrintWriter pw, int which) {
+    public final void dumpCheckinLocked(PrintWriter pw, int which, int reqUid) {
         final long rawUptime = SystemClock.uptimeMillis() * 1000;
         final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptime(rawUptime);
@@ -856,19 +853,24 @@
                     getDischargeCurrentLevel());
         }
         
-        Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
-        if (kernelWakelocks.size() > 0) {
-            for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                sb.setLength(0);
-                printWakeLockCheckin(sb, ent.getValue(), batteryRealtime, null, which, "");
-
-                dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
-                        sb.toString());
+        if (reqUid < 0) {
+            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                    sb.setLength(0);
+                    printWakeLockCheckin(sb, ent.getValue(), batteryRealtime, null, which, "");
+    
+                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
+                            sb.toString());
+                }
             }
         }
         
         for (int iu = 0; iu < NU; iu++) {
             final int uid = uidStats.keyAt(iu);
+            if (reqUid >= 0 && uid != reqUid) {
+                continue;
+            }
             Uid u = uidStats.valueAt(iu);
             // Dump Network stats per uid, if any
             long rx = u.getTcpBytesReceived(which);
@@ -987,7 +989,7 @@
     }
 
     @SuppressWarnings("unused")
-    private final void dumpLocked(PrintWriter pw, String prefix, int which) {
+    public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) {
         final long rawUptime = SystemClock.uptimeMillis() * 1000;
         final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptime(rawUptime);
@@ -1063,23 +1065,25 @@
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
         
-        Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
-        if (kernelWakelocks.size() > 0) {
-            for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                
-                String linePrefix = ": ";
-                sb.setLength(0);
-                sb.append(prefix);
-                sb.append("  Kernel Wake lock ");
-                sb.append(ent.getKey());
-                linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, 
-                        linePrefix);
-                if (!linePrefix.equals(": ")) {
-                    sb.append(" realtime");
-                } else {
-                    sb.append(": (nothing executed)");
+        if (reqUid < 0) {
+            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                    
+                    String linePrefix = ": ";
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("  Kernel Wake lock ");
+                    sb.append(ent.getKey());
+                    linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, 
+                            linePrefix);
+                    if (!linePrefix.equals(": ")) {
+                        sb.append(" realtime");
+                    } else {
+                        sb.append(": (nothing executed)");
+                    }
+                    pw.println(sb.toString());
                 }
-                pw.println(sb.toString());
             }
         }
     
@@ -1212,7 +1216,12 @@
 
         for (int iu=0; iu<NU; iu++) {
             final int uid = uidStats.keyAt(iu);
+            if (reqUid >= 0 && uid != reqUid) {
+                continue;
+            }
+            
             Uid u = uidStats.valueAt(iu);
+            
             pw.println(prefix + "  #" + uid + ":");
             boolean uidActivity = false;
             
@@ -1421,16 +1430,16 @@
         pw.println("Total Statistics (Current and Historic):");
         pw.println("  System starts: " + getStartCount()
                 + ", currently on battery: " + getIsOnBattery());
-        dumpLocked(pw, "", STATS_TOTAL);
+        dumpLocked(pw, "", STATS_TOTAL, -1);
         pw.println("");
         pw.println("Last Run Statistics (Previous run of system):");
-        dumpLocked(pw, "", STATS_LAST);
+        dumpLocked(pw, "", STATS_LAST, -1);
         pw.println("");
         pw.println("Current Battery Statistics (Currently running system):");
-        dumpLocked(pw, "", STATS_CURRENT);
+        dumpLocked(pw, "", STATS_CURRENT, -1);
         pw.println("");
         pw.println("Unplugged Statistics (Since last unplugged from power):");
-        dumpLocked(pw, "", STATS_UNPLUGGED);
+        dumpLocked(pw, "", STATS_UNPLUGGED, -1);
     }
     
     @SuppressWarnings("unused")
@@ -1445,13 +1454,13 @@
         }
         
         if (isUnpluggedOnly) {
-            dumpCheckinLocked(pw, STATS_UNPLUGGED);
+            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
         }
         else {
-            dumpCheckinLocked(pw, STATS_TOTAL);
-            dumpCheckinLocked(pw, STATS_LAST);
-            dumpCheckinLocked(pw, STATS_UNPLUGGED);
-            dumpCheckinLocked(pw, STATS_CURRENT);
+            dumpCheckinLocked(pw, STATS_TOTAL, -1);
+            dumpCheckinLocked(pw, STATS_LAST, -1);
+            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
+            dumpCheckinLocked(pw, STATS_CURRENT, -1);
         }
     }
     
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
new file mode 100644
index 0000000..343041f
--- /dev/null
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -0,0 +1,287 @@
+package com.android.internal.content;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+
+import java.util.HashSet;
+
+/**
+ * Helper class for monitoring the state of packages: adding, removing,
+ * updating, and disappearing and reappearing on the SD card.
+ */
+public abstract class PackageMonitor extends android.content.BroadcastReceiver {
+    static final IntentFilter sPackageFilt = new IntentFilter();
+    static final IntentFilter sNonDataFilt = new IntentFilter();
+    static final IntentFilter sExternalFilt = new IntentFilter();
+    
+    static {
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
+        sPackageFilt.addDataScheme("package");
+        sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+    }
+    
+    final HashSet<String> mUpdatingPackages = new HashSet<String>();
+    
+    Context mRegisteredContext;
+    String[] mDisappearingPackages;
+    String[] mAppearingPackages;
+    String[] mModifiedPackages;
+    int mChangeType;
+    boolean mSomePackagesChanged;
+    
+    String[] mTempArray = new String[1];
+    
+    public void register(Context context, boolean externalStorage) {
+        if (mRegisteredContext != null) {
+            throw new IllegalStateException("Already registered");
+        }
+        mRegisteredContext = context;
+        context.registerReceiver(this, sPackageFilt);
+        context.registerReceiver(this, sNonDataFilt);
+        if (externalStorage) {
+            context.registerReceiver(this, sExternalFilt);
+        }
+    }
+    
+    public void unregister() {
+        if (mRegisteredContext == null) {
+            throw new IllegalStateException("Not registered");
+        }
+        mRegisteredContext.unregisterReceiver(this);
+        mRegisteredContext = null;
+    }
+    
+    //not yet implemented
+    boolean isPackageUpdating(String packageName) {
+        synchronized (mUpdatingPackages) {
+            return mUpdatingPackages.contains(packageName);
+        }
+    }
+    
+    public void onBeginPackageChanges() {
+    }
+    
+    public void onPackageAdded(String packageName, int uid) {
+    }
+    
+    public void onPackageRemoved(String packageName, int uid) {
+    }
+    
+    public void onPackageUpdateStarted(String packageName, int uid) {
+    }
+    
+    public void onPackageUpdateFinished(String packageName, int uid) {
+    }
+    
+    public void onPackageChanged(String packageName, int uid, String[] components) {
+    }
+    
+    public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+        return false;
+    }
+    
+    public void onUidRemoved(int uid) {
+    }
+    
+    public void onPackagesAvailable(String[] packages) {
+    }
+    
+    public void onPackagesUnavailable(String[] packages) {
+    }
+    
+    public static final int PACKAGE_UNCHANGED = 0;
+    public static final int PACKAGE_UPDATING = 1;
+    public static final int PACKAGE_TEMPORARY_CHANGE = 2;
+    public static final int PACKAGE_PERMANENT_CHANGE = 3;
+    
+    public void onPackageDisappeared(String packageName, int reason) {
+    }
+    
+    public void onPackageAppeared(String packageName, int reason) {
+    }
+    
+    public void onPackageModified(String packageName) {
+    }
+    
+    public boolean didSomePackagesChange() {
+        return mSomePackagesChanged;
+    }
+    
+    public int isPackageAppearing(String packageName) {
+        if (mAppearingPackages != null) {
+            for (int i=mAppearingPackages.length-1; i>=0; i--) {
+                if (packageName.equals(mAppearingPackages[i])) {
+                    return mChangeType;
+                }
+            }
+        }
+        return PACKAGE_UNCHANGED;
+    }
+    
+    public boolean anyPackagesAppearing() {
+        return mAppearingPackages != null;
+    }
+    
+    public int isPackageDisappearing(String packageName) {
+        if (mDisappearingPackages != null) {
+            for (int i=mDisappearingPackages.length-1; i>=0; i--) {
+                if (packageName.equals(mDisappearingPackages[i])) {
+                    return mChangeType;
+                }
+            }
+        }
+        return PACKAGE_UNCHANGED;
+    }
+    
+    public boolean anyPackagesDisappearing() {
+        return mDisappearingPackages != null;
+    }
+    
+    public boolean isPackageModified(String packageName) {
+        if (mModifiedPackages != null) {
+            for (int i=mModifiedPackages.length-1; i>=0; i--) {
+                if (packageName.equals(mModifiedPackages[i])) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    public void onSomePackagesChanged() {
+    }
+    
+    public void onFinishPackageChanges() {
+    }
+    
+    String getPackageName(Intent intent) {
+        Uri uri = intent.getData();
+        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+        return pkg;
+    }
+    
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        onBeginPackageChanges();
+        
+        mDisappearingPackages = mAppearingPackages = null;
+        mSomePackagesChanged = false;
+        
+        String action = intent.getAction();
+        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            // We consider something to have changed regardless of whether
+            // this is just an update, because the update is now finished
+            // and the contents of the package may have changed.
+            mSomePackagesChanged = true;
+            if (pkg != null) {
+                mAppearingPackages = mTempArray;
+                mTempArray[0] = pkg;
+                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    mModifiedPackages = mTempArray;
+                    mChangeType = PACKAGE_UPDATING;
+                    onPackageUpdateFinished(pkg, uid);
+                    onPackageModified(pkg);
+                } else {
+                    mChangeType = PACKAGE_PERMANENT_CHANGE;
+                    onPackageAdded(pkg, uid);
+                }
+                onPackageAppeared(pkg, mChangeType);
+                if (mChangeType == PACKAGE_UPDATING) {
+                    synchronized (mUpdatingPackages) {
+                        mUpdatingPackages.remove(pkg);
+                    }
+                }
+            }
+        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            if (pkg != null) {
+                mDisappearingPackages = mTempArray;
+                mTempArray[0] = pkg;
+                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    mChangeType = PACKAGE_UPDATING;
+                    synchronized (mUpdatingPackages) {
+                        //not used for now
+                        //mUpdatingPackages.add(pkg);
+                    }
+                    onPackageUpdateStarted(pkg, uid);
+                } else {
+                    mChangeType = PACKAGE_PERMANENT_CHANGE;
+                    // We only consider something to have changed if this is
+                    // not a replace; for a replace, we just need to consider
+                    // it when it is re-added.
+                    mSomePackagesChanged = true;
+                    onPackageRemoved(pkg, uid);
+                }
+                onPackageDisappeared(pkg, mChangeType);
+            }
+        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            String[] components = intent.getStringArrayExtra(
+                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+            if (pkg != null) {
+                mModifiedPackages = mTempArray;
+                mTempArray[0] = pkg;
+                onPackageChanged(pkg, uid, components);
+                // XXX Don't want this to always cause mSomePackagesChanged,
+                // since it can happen a fair amount.
+                onPackageModified(pkg);
+            }
+        } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
+            mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            boolean canRestart = onHandleForceStop(intent,
+                    mDisappearingPackages,
+                    intent.getIntExtra(Intent.EXTRA_UID, 0), false);
+            if (canRestart) setResultCode(Activity.RESULT_OK);
+        } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
+            mDisappearingPackages = new String[] {getPackageName(intent)};
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            onHandleForceStop(intent, mDisappearingPackages,
+                    intent.getIntExtra(Intent.EXTRA_UID, 0), true);
+        } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
+            onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            mAppearingPackages = pkgList;
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            mSomePackagesChanged = true;
+            if (pkgList != null) {
+                onPackagesAvailable(pkgList);
+                for (int i=0; i<pkgList.length; i++) {
+                    onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
+                }
+            }
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            mDisappearingPackages = pkgList;
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            mSomePackagesChanged = true;
+            if (pkgList != null) {
+                onPackagesUnavailable(pkgList);
+                for (int i=0; i<pkgList.length; i++) {
+                    onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
+                }
+            }
+        }
+        
+        if (mSomePackagesChanged) {
+            onSomePackagesChanged();
+        }
+        
+        onFinishPackageChanges();
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index cf34d4e..5b616b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -618,18 +618,9 @@
 
        if (upgradeVersion == 48) {
            /*
-            * Adding a new setting for which voice recognition service to use.
+            * Default recognition service no longer initialized here,
+            * moved to RecognitionManagerService.
             */
-           db.beginTransaction();
-           try {
-               SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
-                       + " VALUES(?,?);");
-               loadVoiceRecognitionServiceSetting(stmt);
-               stmt.close();
-               db.setTransactionSuccessful();
-           } finally {
-               db.endTransaction();
-           }
            upgradeVersion = 49;
        }
 
@@ -1028,8 +1019,6 @@
         loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
                 R.bool.def_mount_ums_notify_enabled);
 
-        loadVoiceRecognitionServiceSetting(stmt);
-
         stmt.close();
     }
 
@@ -1041,32 +1030,6 @@
                 R.string.def_backup_transport);
     }
 
-    /**
-     * Introduced in database version 49.
-     */
-    private void loadVoiceRecognitionServiceSetting(SQLiteStatement stmt) {
-        String selectedService = null;
-        List<ResolveInfo> availableRecognitionServices =
-                mContext.getPackageManager().queryIntentServices(
-                        new Intent(RecognitionService.SERVICE_INTERFACE), 0);
-        int numAvailable = availableRecognitionServices.size();
-
-        if (numAvailable == 0) {
-            Log.w(TAG, "no available voice recognition services found");
-        } else {
-            if (numAvailable > 1) {
-                Log.w(TAG, "more than one voice recognition service found, picking first");
-            }
-
-            ServiceInfo serviceInfo = availableRecognitionServices.get(0).serviceInfo;
-            selectedService =
-                    new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToString();
-        }
-
-        loadSetting(stmt, Settings.Secure.VOICE_RECOGNITION_SERVICE,
-                selectedService == null ? "" : selectedService);
-    }
-
     private void loadSetting(SQLiteStatement stmt, String key, Object value) {
         stmt.bindString(1, key);
         stmt.bindString(2, value.toString());
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4080a6a..db802d3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,7 +20,6 @@
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
-import java.util.Random;
 
 import android.backup.BackupManager;
 import android.content.ContentProvider;
@@ -30,14 +29,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
-import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
-import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.DrmStore;
 import android.provider.MediaStore;
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index 477ea0c..c55dcb3 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.HandlerCaller.SomeArgs;
 
@@ -56,6 +57,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -145,13 +147,58 @@
     private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
         Context context = mContext;
 
-        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+        PackageMonitor monitor = new PackageMonitor() {
             @Override
-            public void onReceive(Context context, Intent intent) {
+            public void onSomePackagesChanged() {
                 synchronized (mLock) {
                     populateAccessibilityServiceListLocked();
+                    manageServicesLocked();
+                }
+            }
+            
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages,
+                    int uid, boolean doit) {
+                synchronized (mLock) {
+                    boolean changed = false;
+                    Iterator<ComponentName> it = mEnabledServices.iterator();
+                    while (it.hasNext()) {
+                        ComponentName comp = it.next();
+                        String compPkg = comp.getPackageName();
+                        for (String pkg : packages) {
+                            if (compPkg.equals(pkg)) {
+                                if (!doit) {
+                                    return true;
+                                }
+                                it.remove();
+                                changed = true;
+                            }
+                        }
+                    }
+                    if (changed) {
+                        it = mEnabledServices.iterator();
+                        StringBuilder str = new StringBuilder();
+                        while (it.hasNext()) {
+                            if (str.length() > 0) {
+                                str.append(':');
+                            }
+                            str.append(it.next().flattenToShortString());
+                        }
+                        Settings.Secure.putString(mContext.getContentResolver(),
+                                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                                str.toString());
+                        manageServicesLocked();
+                    }
+                    return false;
+                }
+            }
+            
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
+                    synchronized (mLock) {
+                        populateAccessibilityServiceListLocked();
 
-                    if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
                         // get the accessibility enabled setting on boot
                         mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
@@ -160,29 +207,23 @@
                         if (mIsEnabled) {
                             updateClientsLocked();
                         }
-                    }
 
-                    manageServicesLocked();
+                        manageServicesLocked();
+                    }
+                    
+                    return;
                 }
+                
+                super.onReceive(context, intent);
             }
         };
 
         // package changes
-        IntentFilter packageFilter = new IntentFilter();
-        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        packageFilter.addDataScheme("package");
-        context.registerReceiver(broadcastReceiver, packageFilter);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(broadcastReceiver, sdFilter);
+        monitor.register(context, true);
 
         // boot completed
         IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-        mContext.registerReceiver(broadcastReceiver, bootFiler);
+        mContext.registerReceiver(monitor, bootFiler);
     }
 
     /**
@@ -529,8 +570,14 @@
             TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
             splitter.setString(servicesValue);
             while (splitter.hasNext()) {
-                ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
-                enabledServices.add(enabledService);
+                String str = splitter.next();
+                if (str == null || str.length() <= 0) {
+                    continue;
+                }
+                ComponentName enabledService = ComponentName.unflattenFromString(str);
+                if (enabledService != null) {
+                    enabledServices.add(enabledService);
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 44cc0bb..f480209 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.app.Activity;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.IAlarmManager;
@@ -344,6 +345,22 @@
         }
     }
     
+    public boolean lookForPackageLocked(String packageName) {
+        return lookForPackageLocked(mRtcWakeupAlarms, packageName)
+                || lookForPackageLocked(mRtcAlarms, packageName)
+                || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
+                || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
+    }
+
+    private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
+        for (int i=alarmList.size()-1; i>=0; i--) {
+            if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
     private ArrayList<Alarm> getAlarmList(int type) {
         switch (type) {
             case AlarmManager.RTC_WAKEUP:              return mRtcWakeupAlarms;
@@ -778,6 +795,7 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
             filter.addDataScheme("package");
             mContext.registerReceiver(this, filter);
              // Register for events related to sdcard installation.
@@ -791,7 +809,16 @@
             synchronized (mLock) {
                 String action = intent.getAction();
                 String pkgList[] = null;
-                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+                    for (String packageName : pkgList) {
+                        if (lookForPackageLocked(packageName)) {
+                            setResultCode(Activity.RESULT_OK);
+                            return;
+                        }
+                    }
+                    return;
+                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else {
                     Uri data = intent.getData();
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index a267e0f..ac65aa9 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -17,6 +17,8 @@
 package com.android.server;
 
 import com.android.common.FastXmlSerializer;
+import com.android.common.XmlUtils;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.widget.LockPatternUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -34,6 +36,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IPowerManager;
@@ -58,9 +61,10 @@
  * Implementation of the device policy APIs.
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
-    private static final String TAG = "DevicePolicyManagerService";
+    static final String TAG = "DevicePolicyManagerService";
     
-    private final Context mContext;
+    final Context mContext;
+    final MyPackageMonitor mMonitor;
 
     IPowerManager mIPowerManager;
     
@@ -89,6 +93,9 @@
         
         void writeToXml(XmlSerializer out)
                 throws IllegalArgumentException, IllegalStateException, IOException {
+            out.startTag(null, "policies");
+            info.writePoliciesToXml(out);
+            out.endTag(null, "policies");
             if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 out.startTag(null, "password-quality");
                 out.attribute(null, "value", Integer.toString(passwordQuality));
@@ -121,7 +128,9 @@
                     continue;
                 }
                 String tag = parser.getName();
-                if ("password-quality".equals(tag)) {
+                if ("policies".equals(tag)) {
+                    info.readPoliciesFromXml(parser);
+                } else if ("password-quality".equals(tag)) {
                     passwordQuality = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
                 } else if ("min-password-length".equals(tag)) {
@@ -133,6 +142,35 @@
                 } else if ("max-failed-password-wipe".equals(tag)) {
                     maximumFailedPasswordsForWipe = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
+                } else {
+                    Log.w(TAG, "Unknown admin tag: " + tag);
+                }
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+    
+    class MyPackageMonitor extends PackageMonitor {
+        public void onSomePackagesChanged() {
+            synchronized (DevicePolicyManagerService.this) {
+                for (int i=mAdminList.size()-1; i>=0; i--) {
+                    ActiveAdmin aa = mAdminList.get(i);
+                    int change = isPackageDisappearing(aa.info.getPackageName()); 
+                    if (change == PACKAGE_PERMANENT_CHANGE
+                            || change == PACKAGE_TEMPORARY_CHANGE) {
+                        Log.w(TAG, "Admin unexpectedly uninstalled: "
+                                + aa.info.getComponent());
+                        mAdminList.remove(i);
+                    } else if (isPackageModified(aa.info.getPackageName())) {
+                        try {
+                            mContext.getPackageManager().getReceiverInfo(
+                                    aa.info.getComponent(), 0);
+                        } catch (NameNotFoundException e) {
+                            Log.w(TAG, "Admin package change removed component: "
+                                    + aa.info.getComponent());
+                            mAdminList.remove(i);
+                        }
+                    }
                 }
             }
         }
@@ -143,6 +181,8 @@
      */
     public DevicePolicyManagerService(Context context) {
         mContext = context;
+        mMonitor = new MyPackageMonitor();
+        mMonitor.register(context, true);
     }
 
     private IPowerManager getIPowerManager() {
@@ -336,6 +376,10 @@
                 } else if ("failed-password-attempts".equals(tag)) {
                     mFailedPasswordAttempts = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
+                    XmlUtils.skipCurrentTag(parser);
+                } else {
+                    Log.w(TAG, "Unknown tag: " + tag);
+                    XmlUtils.skipCurrentTag(parser);
                 }
             }
         } catch (NullPointerException e) {
@@ -420,6 +464,18 @@
         }
     }
     
+    public boolean packageHasActiveAdmins(String packageName) {
+        synchronized (this) {
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                if (mAdminList.get(i).info.getPackageName().equals(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+    
     public void removeActiveAdmin(ComponentName adminReceiver) {
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 5e96a2f..59d4c9b 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethod;
@@ -48,7 +49,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -332,51 +332,68 @@
         }
     }
 
-    class PackageReceiver extends android.content.BroadcastReceiver {
+    class MyPackageMonitor extends PackageMonitor {
+        
         @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            String pkgList[] = null;
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                Uri uri = intent.getData();
-                String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
-                if (pkg != null) {
-                    pkgList = new String[] { pkg };
-                }
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
-                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            }
-            if (pkgList == null || pkgList.length == 0) {
-                return;
-            }
+        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
             synchronized (mMethodMap) {
-                buildInputMethodListLocked(mMethodList, mMethodMap);
-
-                InputMethodInfo curIm = null;
-                String curInputMethodId = Settings.Secure.getString(context
+                String curInputMethodId = Settings.Secure.getString(mContext
                         .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
                 final int N = mMethodList.size();
                 if (curInputMethodId != null) {
                     for (int i=0; i<N; i++) {
-                        if (mMethodList.get(i).getId().equals(curInputMethodId)) {
-                            curIm = mMethodList.get(i);
+                        InputMethodInfo imi = mMethodList.get(i);
+                        if (imi.getId().equals(curInputMethodId)) {
+                            for (String pkg : packages) {
+                                if (imi.getPackageName().equals(pkg)) {
+                                    if (!doit) {
+                                        return true;
+                                    }
+                                    
+                                    Settings.Secure.putString(mContext.getContentResolver(),
+                                            Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                    chooseNewDefaultIMELocked();
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            synchronized (mMethodMap) {
+                InputMethodInfo curIm = null;
+                String curInputMethodId = Settings.Secure.getString(mContext
+                        .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+                final int N = mMethodList.size();
+                if (curInputMethodId != null) {
+                    for (int i=0; i<N; i++) {
+                        InputMethodInfo imi = mMethodList.get(i);
+                        if (imi.getId().equals(curInputMethodId)) {
+                            curIm = imi;
+                        }
+                        int change = isPackageDisappearing(imi.getPackageName());
+                        if (change == PACKAGE_TEMPORARY_CHANGE
+                                || change == PACKAGE_PERMANENT_CHANGE) {
+                            Log.i(TAG, "Input method uninstalled, disabling: "
+                                    + imi.getComponent());
+                            setInputMethodEnabledLocked(imi.getId(), false);
                         }
                     }
                 }
 
+                buildInputMethodListLocked(mMethodList, mMethodMap);
+
                 boolean changed = false;
 
                 if (curIm != null) {
-                    boolean foundPkg = false;
-                    for (String pkg : pkgList) {
-                        if (curIm.getPackageName().equals(pkg)) {
-                            foundPkg = true;
-                            break;
-                        }
-                    }
-                    if (foundPkg) {
+                    int change = isPackageDisappearing(curIm.getPackageName()); 
+                    if (change == PACKAGE_TEMPORARY_CHANGE
+                            || change == PACKAGE_PERMANENT_CHANGE) {
                         ServiceInfo si = null;
                         try {
                             si = mContext.getPackageManager().getServiceInfo(
@@ -387,7 +404,7 @@
                             // Uh oh, current input method is no longer around!
                             // Pick another one...
                             Log.i(TAG, "Current input method removed: " + curInputMethodId);
-                            if (!chooseNewDefaultIME()) {
+                            if (!chooseNewDefaultIMELocked()) {
                                 changed = true;
                                 curIm = null;
                                 curInputMethodId = "";
@@ -397,16 +414,17 @@
                                         curInputMethodId);
                             }
                         }
-
-                    } else if (curIm == null) {
-                        // We currently don't have a default input method... is
-                        // one now available?
-                        changed = chooseNewDefaultIME();
                     }
+                }
+                
+                if (curIm == null) {
+                    // We currently don't have a default input method... is
+                    // one now available?
+                    changed = chooseNewDefaultIMELocked();
+                }
 
-                    if (changed) {
-                        updateFromSettingsLocked();
-                    }
+                if (changed) {
+                    updateFromSettingsLocked();
                 }
             }
         }
@@ -438,19 +456,7 @@
             }
         });
 
-        PackageReceiver mBroadcastReceiver = new PackageReceiver();
-        IntentFilter packageFilt = new IntentFilter();
-        packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        packageFilt.addDataScheme("package");
-        mContext.registerReceiver(mBroadcastReceiver, packageFilt);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+        (new MyPackageMonitor()).register(mContext, true);
 
         IntentFilter screenOnOffFilt = new IntentFilter();
         screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
@@ -1385,7 +1391,7 @@
                 & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
-    private boolean chooseNewDefaultIME() {
+    private boolean chooseNewDefaultIMELocked() {
         List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
         if (enabled != null && enabled.size() > 0) {
             Settings.Secure.putString(mContext.getContentResolver(),
@@ -1446,7 +1452,7 @@
         String defaultIme = Settings.Secure.getString(mContext
                 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
         if (!map.containsKey(defaultIme)) {
-            if (chooseNewDefaultIME()) {
+            if (chooseNewDefaultIMELocked()) {
                 updateFromSettingsLocked();
             }
         }
@@ -1557,91 +1563,95 @@
                         "Requires permission "
                         + android.Manifest.permission.WRITE_SECURE_SETTINGS);
             }
-
+            
             long ident = Binder.clearCallingIdentity();
             try {
-                // Make sure this is a valid input method.
-                InputMethodInfo imm = mMethodMap.get(id);
-                if (imm == null) {
-                    if (imm == null) {
-                        throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
-                    }
-                }
-
-                StringBuilder builder = new StringBuilder(256);
-
-                boolean removed = false;
-                String firstId = null;
-
-                // Look through the currently enabled input methods.
-                String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
-                        Settings.Secure.ENABLED_INPUT_METHODS);
-                if (enabledStr != null) {
-                    final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
-                    splitter.setString(enabledStr);
-                    while (splitter.hasNext()) {
-                        String curId = splitter.next();
-                        if (curId.equals(id)) {
-                            if (enabled) {
-                                // We are enabling this input method, but it is
-                                // already enabled.  Nothing to do.  The previous
-                                // state was enabled.
-                                return true;
-                            }
-                            // We are disabling this input method, and it is
-                            // currently enabled.  Skip it to remove from the
-                            // new list.
-                            removed = true;
-                        } else if (!enabled) {
-                            // We are building a new list of input methods that
-                            // doesn't contain the given one.
-                            if (firstId == null) firstId = curId;
-                            if (builder.length() > 0) builder.append(':');
-                            builder.append(curId);
-                        }
-                    }
-                }
-
-                if (!enabled) {
-                    if (!removed) {
-                        // We are disabling the input method but it is already
-                        // disabled.  Nothing to do.  The previous state was
-                        // disabled.
-                        return false;
-                    }
-                    // Update the setting with the new list of input methods.
-                    Settings.Secure.putString(mContext.getContentResolver(),
-                            Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
-                    // We the disabled input method is currently selected, switch
-                    // to another one.
-                    String selId = Settings.Secure.getString(mContext.getContentResolver(),
-                            Settings.Secure.DEFAULT_INPUT_METHOD);
-                    if (id.equals(selId)) {
-                        Settings.Secure.putString(mContext.getContentResolver(),
-                                Settings.Secure.DEFAULT_INPUT_METHOD,
-                                firstId != null ? firstId : "");
-                    }
-                    // Previous state was enabled.
-                    return true;
-                }
-
-                // Add in the newly enabled input method.
-                if (enabledStr == null || enabledStr.length() == 0) {
-                    enabledStr = id;
-                } else {
-                    enabledStr = enabledStr + ':' + id;
-                }
-
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
-
-                // Previous state was disabled.
-                return false;
+                return setInputMethodEnabledLocked(id, enabled);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
     }
+    
+    boolean setInputMethodEnabledLocked(String id, boolean enabled) {
+        // Make sure this is a valid input method.
+        InputMethodInfo imm = mMethodMap.get(id);
+        if (imm == null) {
+            if (imm == null) {
+                throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+            }
+        }
+
+        StringBuilder builder = new StringBuilder(256);
+
+        boolean removed = false;
+        String firstId = null;
+
+        // Look through the currently enabled input methods.
+        String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_INPUT_METHODS);
+        if (enabledStr != null) {
+            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(enabledStr);
+            while (splitter.hasNext()) {
+                String curId = splitter.next();
+                if (curId.equals(id)) {
+                    if (enabled) {
+                        // We are enabling this input method, but it is
+                        // already enabled.  Nothing to do.  The previous
+                        // state was enabled.
+                        return true;
+                    }
+                    // We are disabling this input method, and it is
+                    // currently enabled.  Skip it to remove from the
+                    // new list.
+                    removed = true;
+                } else if (!enabled) {
+                    // We are building a new list of input methods that
+                    // doesn't contain the given one.
+                    if (firstId == null) firstId = curId;
+                    if (builder.length() > 0) builder.append(':');
+                    builder.append(curId);
+                }
+            }
+        }
+
+        if (!enabled) {
+            if (!removed) {
+                // We are disabling the input method but it is already
+                // disabled.  Nothing to do.  The previous state was
+                // disabled.
+                return false;
+            }
+            // Update the setting with the new list of input methods.
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
+            // We the disabled input method is currently selected, switch
+            // to another one.
+            String selId = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.DEFAULT_INPUT_METHOD);
+            if (id.equals(selId)) {
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.DEFAULT_INPUT_METHOD,
+                        firstId != null ? firstId : "");
+            }
+            // Previous state was enabled.
+            return true;
+        }
+
+        // Add in the newly enabled input method.
+        if (enabledStr == null || enabledStr.length() == 0) {
+            enabledStr = id;
+        } else {
+            enabledStr = enabledStr + ':' + id;
+        }
+
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
+
+        // Previous state was disabled.
+        return false;
+    }
 
     // ----------------------------------------------------------------------
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 004fcf13..dd351be 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -27,6 +27,7 @@
 import java.util.Observer;
 import java.util.Set;
 
+import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -506,6 +507,7 @@
         // Register for Package Manager updates
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
@@ -1539,8 +1541,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-
-            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+            boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART);
+            if (queryRestart
+                    || action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 synchronized (mLock) {
@@ -1560,6 +1563,10 @@
                                 for (int j=i.size()-1; j>=0; j--) {
                                     UpdateRecord ur = i.get(j);
                                     if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
+                                        if (queryRestart) {
+                                            setResultCode(Activity.RESULT_OK);
+                                            return;
+                                        }
                                         if (removedRecs == null) {
                                             removedRecs = new ArrayList<Receiver>();
                                         }
@@ -1572,6 +1579,10 @@
                             ArrayList<ProximityAlert> removedAlerts = null;
                             for (ProximityAlert i : mProximityAlerts.values()) {
                                 if (i.mUid == uid) {
+                                    if (queryRestart) {
+                                        setResultCode(Activity.RESULT_OK);
+                                        return;
+                                    }
                                     if (removedAlerts == null) {
                                         removedAlerts = new ArrayList<ProximityAlert>();
                                     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 3657133..3c43352 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -307,6 +307,8 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
+            boolean queryRestart = false;
+            
             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                 boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
                 int level = intent.getIntExtra("level", -1);
@@ -330,10 +332,13 @@
                 updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
+                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 String pkgList[] = null;
                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                } else if (queryRestart) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
                 } else {
                     Uri uri = intent.getData();
                     if (uri == null) {
@@ -347,7 +352,7 @@
                 }
                 if (pkgList != null && (pkgList.length > 0)) {
                     for (String pkgName : pkgList) {
-                        cancelAllNotificationsInt(pkgName, 0, 0);
+                        cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
                     }
                 }
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
@@ -436,11 +441,15 @@
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(Intent.ACTION_UMS_CONNECTED);
         filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         mContext.registerReceiver(mIntentReceiver, filter);
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+        pkgFilter.addDataScheme("package");
+        mContext.registerReceiver(mIntentReceiver, pkgFilter);
         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mIntentReceiver, sdFilter);
 
@@ -920,8 +929,8 @@
      * Cancels all notifications from a given package that have all of the
      * {@code mustHaveFlags}.
      */
-    void cancelAllNotificationsInt(String pkg, int mustHaveFlags,
-            int mustNotHaveFlags) {
+    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
+            int mustNotHaveFlags, boolean doit) {
         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags);
 
         synchronized (mNotificationList) {
@@ -938,13 +947,17 @@
                 if (!r.pkg.equals(pkg)) {
                     continue;
                 }
+                canceledSomething = true;
+                if (!doit) {
+                    return true;
+                }
                 mNotificationList.remove(i);
                 cancelNotificationLocked(r);
-                canceledSomething = true;
             }
             if (canceledSomething) {
                 updateLightsLocked();
             }
+            return canceledSomething;
         }
     }
 
@@ -966,7 +979,7 @@
 
         // Calling from user space, don't allow the canceling of actively
         // running foreground services.
-        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE);
+        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
     }
 
     void checkIncomingCall(String pkg) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 38d8615..4fdcd59 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -28,7 +28,9 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.app.ActivityManagerNative;
+import android.app.DevicePolicyManager;
 import android.app.IActivityManager;
+import android.app.IDevicePolicyManager;
 import android.backup.IBackupManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -5594,6 +5596,16 @@
         PackageRemovedInfo info = new PackageRemovedInfo();
         boolean res;
 
+        IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+        try {
+            if (dpm != null && dpm.packageHasActiveAdmins(packageName)) {
+                Log.w(TAG, "Not removing package " + packageName + ": has active device admin");
+                return false;
+            }
+        } catch (RemoteException e) {
+        }
+        
         synchronized (mInstallLock) {
             res = deletePackageLI(packageName, deleteCodeAndResources, flags, info);
         }
diff --git a/services/java/com/android/server/RecognitionManagerService.java b/services/java/com/android/server/RecognitionManagerService.java
new file mode 100644
index 0000000..7305b07
--- /dev/null
+++ b/services/java/com/android/server/RecognitionManagerService.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.android.internal.content.PackageMonitor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.provider.Settings;
+import android.speech.RecognitionService;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+public class RecognitionManagerService extends Binder {
+    final static String TAG = "RecognitionManagerService";
+    
+    final Context mContext;
+    final MyPackageMonitor mMonitor;
+    
+    class MyPackageMonitor extends PackageMonitor {
+        public void onSomePackagesChanged() {
+            ComponentName comp = getCurRecognizer();
+            if (comp == null) {
+                if (anyPackagesAppearing()) {
+                    comp = findAvailRecognizer(null);
+                    if (comp != null) {
+                        setCurRecognizer(comp);
+                    }
+                }
+                return;
+            }
+            
+            int change = isPackageDisappearing(comp.getPackageName()); 
+            if (change == PACKAGE_PERMANENT_CHANGE
+                    || change == PACKAGE_TEMPORARY_CHANGE) {
+                setCurRecognizer(findAvailRecognizer(null));
+                
+            } else if (isPackageModified(comp.getPackageName())) {
+                setCurRecognizer(findAvailRecognizer(comp.getPackageName()));
+            }
+        }
+    }
+    
+    RecognitionManagerService(Context context) {
+        mContext = context;
+        mMonitor = new MyPackageMonitor();
+        mMonitor.register(context, true);
+    }
+    
+    public void systemReady() {
+        ComponentName comp = getCurRecognizer();
+        if (comp != null) {
+            // See if the current recognizer is no longer available.
+            try {
+                mContext.getPackageManager().getServiceInfo(comp, 0);
+            } catch (NameNotFoundException e) {
+                setCurRecognizer(null);
+            }
+        } else {
+            comp = findAvailRecognizer(null);
+            if (comp != null) {
+                setCurRecognizer(comp);
+            }
+        }
+    }
+    
+    ComponentName findAvailRecognizer(String prefPackage) {
+        List<ResolveInfo> available =
+                mContext.getPackageManager().queryIntentServices(
+                        new Intent(RecognitionService.SERVICE_INTERFACE), 0);
+        int numAvailable = available.size();
+    
+        if (numAvailable == 0) {
+            Log.w(TAG, "no available voice recognition services found");
+            return null;
+        } else {
+            if (prefPackage != null) {
+                for (int i=0; i<numAvailable; i++) {
+                    ServiceInfo serviceInfo = available.get(i).serviceInfo;
+                    if (prefPackage.equals(serviceInfo.packageName)) {
+                        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+                    }
+                }
+            }
+            if (numAvailable > 1) {
+                Log.w(TAG, "more than one voice recognition service found, picking first");
+            }
+    
+            ServiceInfo serviceInfo = available.get(0).serviceInfo;
+            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        }
+    }
+    
+    ComponentName getCurRecognizer() {
+        String curRecognizer = Settings.Secure.getString(
+                mContext.getContentResolver(),
+                Settings.Secure.VOICE_RECOGNITION_SERVICE);
+        if (TextUtils.isEmpty(curRecognizer)) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(curRecognizer);
+    }
+    
+    void setCurRecognizer(ComponentName comp) {
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.VOICE_RECOGNITION_SERVICE,
+                comp != null ? comp.flattenToShortString() : "");
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a09896a..38df02f9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@
         BluetoothA2dpService bluetoothA2dp = null;
         HeadsetObserver headset = null;
         DockObserver dock = null;
+        RecognitionManagerService recognition = null;
 
         // Critical services...
         try {
@@ -377,6 +378,13 @@
             }
 
             try {
+                Log.i(TAG, "Recognition Service");
+                recognition = new RecognitionManagerService(context);
+            } catch (Throwable e) {
+                Log.e(TAG, "Failure starting Recognition Service", e);
+            }
+
+            try {
                 com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
             } catch (Throwable e) {
                 Log.e(TAG, "Failure installing status bar icons", e);
@@ -435,6 +443,7 @@
         final AppWidgetService appWidgetF = appWidget;
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
+        final RecognitionManagerService recognitionF = recognition;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -449,6 +458,7 @@
                 if (batteryF != null) batteryF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
                 if (dockF != null) dockF.systemReady();
+                if (recognitionF != null) recognitionF.systemReady();
                 Watchdog.getInstance().start();
 
                 // It is now okay to let the various system services start their
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 81255ee..481e6a4 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -65,7 +65,10 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.service.wallpaper.ImageWallpaper;
+import com.android.server.DevicePolicyManagerService.ActiveAdmin;
+import com.android.server.DevicePolicyManagerService.MyPackageMonitor;
 import com.android.common.FastXmlSerializer;
 
 class WallpaperManagerService extends IWallpaperManager.Stub {
@@ -122,6 +125,7 @@
     
     final Context mContext;
     final IWindowManager mIWindowManager;
+    final MyPackageMonitor mMonitor;
 
     int mWidth = -1;
     int mHeight = -1;
@@ -150,6 +154,7 @@
     
     WallpaperConnection mWallpaperConnection;
     long mLastDiedTime;
+    boolean mWallpaperUpdating;
     
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
@@ -165,6 +170,7 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             synchronized (mLock) {
                 if (mWallpaperConnection == this) {
+                    mLastDiedTime = SystemClock.uptimeMillis();
                     mService = IWallpaperService.Stub.asInterface(service);
                     attachServiceLocked(this);
                     // XXX should probably do saveSettingsLocked() later
@@ -182,8 +188,8 @@
                 mEngine = null;
                 if (mWallpaperConnection == this) {
                     Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
-                    if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
-                            < SystemClock.uptimeMillis()) {
+                    if (!mWallpaperUpdating && (mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
+                                > SystemClock.uptimeMillis()) {
                         Log.w(TAG, "Reverting to built-in wallpaper!");
                         bindWallpaperComponentLocked(null);
                     }
@@ -205,11 +211,92 @@
         }
     }
     
+    class MyPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageUpdateFinished(String packageName, int uid) {
+            synchronized (mLock) {
+                if (mWallpaperComponent != null &&
+                        mWallpaperComponent.getPackageName().equals(packageName)) {
+                    mWallpaperUpdating = false;
+                    ComponentName comp = mWallpaperComponent;
+                    clearWallpaperComponentLocked();
+                    bindWallpaperComponentLocked(comp);
+                }
+            }
+        }
+
+        @Override
+        public void onPackageUpdateStarted(String packageName, int uid) {
+            synchronized (mLock) {
+                if (mWallpaperComponent != null &&
+                        mWallpaperComponent.getPackageName().equals(packageName)) {
+                    mWallpaperUpdating = true;
+                }
+            }
+        }
+
+        @Override
+        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+            return doPackagesChanged(doit);
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            doPackagesChanged(true);
+        }
+        
+        boolean doPackagesChanged(boolean doit) {
+            boolean changed = false;
+            synchronized (mLock) {
+                if (mWallpaperComponent != null) {
+                    int change = isPackageDisappearing(mWallpaperComponent.getPackageName());
+                    if (change == PACKAGE_PERMANENT_CHANGE
+                            || change == PACKAGE_TEMPORARY_CHANGE) {
+                        changed = true;
+                        if (doit) {
+                            Log.w(TAG, "Wallpaper uninstalled, removing: " + mWallpaperComponent);
+                            clearWallpaperLocked();
+                        }
+                    }
+                }
+                if (mNextWallpaperComponent != null) {
+                    int change = isPackageDisappearing(mNextWallpaperComponent.getPackageName());
+                    if (change == PACKAGE_PERMANENT_CHANGE
+                            || change == PACKAGE_TEMPORARY_CHANGE) {
+                        mNextWallpaperComponent = null;
+                    }
+                }
+                if (mWallpaperComponent != null
+                        && isPackageModified(mWallpaperComponent.getPackageName())) {
+                    try {
+                        mContext.getPackageManager().getServiceInfo(
+                                mWallpaperComponent, 0);
+                    } catch (NameNotFoundException e) {
+                        Log.w(TAG, "Wallpaper component gone, removing: " + mWallpaperComponent);
+                        clearWallpaperLocked();
+                    }
+                }
+                if (mNextWallpaperComponent != null
+                        && isPackageModified(mNextWallpaperComponent.getPackageName())) {
+                    try {
+                        mContext.getPackageManager().getServiceInfo(
+                                mNextWallpaperComponent, 0);
+                    } catch (NameNotFoundException e) {
+                        mNextWallpaperComponent = null;
+                    }
+                }
+            }
+            return changed;
+        }
+    }
+    
     public WallpaperManagerService(Context context) {
         if (DEBUG) Log.v(TAG, "WallpaperService startup");
         mContext = context;
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
+        mMonitor = new MyPackageMonitor();
+        mMonitor.register(context, true);
         WALLPAPER_DIR.mkdirs();
         loadSettingsLocked();
         mWallpaperObserver.startWatching();
@@ -241,16 +328,20 @@
     public void clearWallpaper() {
         if (DEBUG) Log.v(TAG, "clearWallpaper");
         synchronized (mLock) {
-            File f = WALLPAPER_FILE;
-            if (f.exists()) {
-                f.delete();
-            }
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                bindWallpaperComponentLocked(null);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            clearWallpaperLocked();
+        }
+    }
+
+    public void clearWallpaperLocked() {
+        File f = WALLPAPER_FILE;
+        if (f.exists()) {
+            f.delete();
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            bindWallpaperComponentLocked(null);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -499,7 +590,9 @@
                     mWidth, mHeight);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed attaching wallpaper; clearing", e);
-            bindWallpaperComponentLocked(null);
+            if (!mWallpaperUpdating) {
+                bindWallpaperComponentLocked(null);
+            }
         }
     }
     
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 45c3f00..5b37f47 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -47,6 +47,7 @@
 import android.app.Service;
 import android.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -93,7 +94,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
@@ -107,7 +107,6 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -296,12 +295,6 @@
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4*1024;
     
-    // System property defining error report receiver for system apps
-    static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
-
-    // System property defining default error report receiver
-    static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
-
     // Corresponding memory levels for above adjustments.
     static final int EMPTY_APP_MEM;
     static final int HIDDEN_APP_MEM;
@@ -1193,7 +1186,7 @@
                     int uid = msg.arg1;
                     boolean restart = (msg.arg2 == 1);
                     String pkg = (String) msg.obj;
-                    forceStopPackageLocked(pkg, uid, restart, false);
+                    forceStopPackageLocked(pkg, uid, restart, false, true);
                 }
             } break;
             }
@@ -1396,7 +1389,8 @@
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
-        mConfiguration.makeDefault();
+        mConfiguration.setToDefaults();
+        mConfiguration.locale = Locale.getDefault();
         mProcessStats.init();
         
         // Add ourself to the Watchdog monitors.
@@ -4846,7 +4840,7 @@
                     return;
                 }
                 killPackageProcessesLocked(packageName, pkgUid,
-                        SECONDARY_SERVER_ADJ, false);
+                        SECONDARY_SERVER_ADJ, false, true);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -4988,7 +4982,7 @@
     }
 
     private void forceStopPackageLocked(final String packageName, int uid) {
-        forceStopPackageLocked(packageName, uid, false, false);
+        forceStopPackageLocked(packageName, uid, false, false, true);
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                 Uri.fromParts("package", packageName, null));
         intent.putExtra(Intent.EXTRA_UID, uid);
@@ -4997,8 +4991,8 @@
                 false, false, MY_PID, Process.SYSTEM_UID);
     }
     
-    private final void killPackageProcessesLocked(String packageName, int uid,
-            int minOomAdj, boolean callerWillRestart) {
+    private final boolean killPackageProcessesLocked(String packageName, int uid,
+            int minOomAdj, boolean callerWillRestart, boolean doit) {
         ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
 
         // Remove all processes this package may have touched: all with the
@@ -5010,11 +5004,16 @@
             for (int ia=0; ia<NA; ia++) {
                 ProcessRecord app = apps.valueAt(ia);
                 if (app.removed) {
-                    procs.add(app);
+                    if (doit) {
+                        procs.add(app);
+                    }
                 } else if ((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
                         || app.processName.equals(packageName)
                         || app.processName.startsWith(procNamePrefix)) {
                     if (app.setAdj >= minOomAdj) {
+                        if (!doit) {
+                            return true;
+                        }
                         app.removed = true;
                         procs.add(app);
                     }
@@ -5026,10 +5025,11 @@
         for (int i=0; i<N; i++) {
             removeProcessLocked(procs.get(i), callerWillRestart);
         }
+        return N > 0;
     }
 
-    private final void forceStopPackageLocked(String name, int uid,
-            boolean callerWillRestart, boolean purgeCache) {
+    private final boolean forceStopPackageLocked(String name, int uid,
+            boolean callerWillRestart, boolean purgeCache, boolean doit) {
         int i, N;
 
         if (uid < 0) {
@@ -5039,21 +5039,28 @@
             }
         }
 
-        Log.i(TAG, "Force stopping package " + name + " uid=" + uid);
+        if (doit) {
+            Log.i(TAG, "Force stopping package " + name + " uid=" + uid);
 
-        Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
-        while (badApps.hasNext()) {
-            SparseArray<Long> ba = badApps.next();
-            if (ba.get(uid) != null) {
-                badApps.remove();
+            Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
+            while (badApps.hasNext()) {
+                SparseArray<Long> ba = badApps.next();
+                if (ba.get(uid) != null) {
+                    badApps.remove();
+                }
             }
         }
-
-        killPackageProcessesLocked(name, uid, -100, callerWillRestart);
+        
+        boolean didSomething = killPackageProcessesLocked(name, uid, -100,
+                callerWillRestart, doit);
         
         for (i=mHistory.size()-1; i>=0; i--) {
             HistoryRecord r = (HistoryRecord)mHistory.get(i);
             if (r.packageName.equals(name)) {
+                if (!doit) {
+                    return true;
+                }
+                didSomething = true;
                 Log.i(TAG, "  Force finishing activity " + r);
                 if (r.app != null) {
                     r.app.removed = true;
@@ -5066,6 +5073,10 @@
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
         for (ServiceRecord service : mServices.values()) {
             if (service.packageName.equals(name)) {
+                if (!doit) {
+                    return true;
+                }
+                didSomething = true;
                 Log.i(TAG, "  Force stopping service " + service);
                 if (service.app != null) {
                     service.app.removed = true;
@@ -5080,13 +5091,17 @@
             bringDownServiceLocked(services.get(i), true);
         }
         
-        resumeTopActivityLocked(null);
-        if (purgeCache) {
-            AttributeCache ac = AttributeCache.instance();
-            if (ac != null) {
-                ac.removePackage(name);
+        if (doit) {
+            if (purgeCache) {
+                AttributeCache ac = AttributeCache.instance();
+                if (ac != null) {
+                    ac.removePackage(name);
+                }
             }
+            resumeTopActivityLocked(null);
         }
+        
+        return didSomething;
     }
 
     private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart) {
@@ -5583,19 +5598,38 @@
     }
 
     final void finishBooting() {
-        // Ensure that any processes we had put on hold are now started
-        // up.
-        final int NP = mProcessesOnHold.size();
-        if (NP > 0) {
-            ArrayList<ProcessRecord> procs =
-                new ArrayList<ProcessRecord>(mProcessesOnHold);
-            for (int ip=0; ip<NP; ip++) {
-                this.startProcessLocked(procs.get(ip), "on-hold", null);
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+        pkgFilter.addDataScheme("package");
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+                if (pkgs != null) {
+                    for (String pkg : pkgs) {
+                        if (forceStopPackageLocked(pkg, -1, false, false, false)) {
+                            setResultCode(Activity.RESULT_OK);
+                            return;
+                        }
+                    }
+                }
             }
-        }
-        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-            // Tell anyone interested that we are done booting!
-            synchronized (this) {
+        }, pkgFilter);
+        
+        synchronized (this) {
+            // Ensure that any processes we had put on hold are now started
+            // up.
+            final int NP = mProcessesOnHold.size();
+            if (NP > 0) {
+                ArrayList<ProcessRecord> procs =
+                    new ArrayList<ProcessRecord>(mProcessesOnHold);
+                for (int ip=0; ip<NP; ip++) {
+                    this.startProcessLocked(procs.get(ip), "on-hold", null);
+                }
+            }
+            
+            if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+                // Tell anyone interested that we are done booting!
                 broadcastIntentLocked(null, null,
                         new Intent(Intent.ACTION_BOOT_COMPLETED, null),
                         null, null, 0, null, null,
@@ -8050,7 +8084,7 @@
             mDebugTransient = !persistent;
             if (packageName != null) {
                 final long origId = Binder.clearCallingIdentity();
-                forceStopPackageLocked(packageName, -1, false, false);
+                forceStopPackageLocked(packageName, -1, false, false, true);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -8339,7 +8373,6 @@
             mAlwaysFinishActivities = alwaysFinishActivities;
             // This happens before any activities are started, so we can
             // change mConfiguration in-place.
-            mConfiguration.locale = Locale.getDefault();
             mConfiguration.updateFrom(configuration);
             mConfigurationSeq = mConfiguration.seq = 1;
             if (DEBUG_CONFIGURATION) Log.v(TAG, "Initial config: " + mConfiguration);
@@ -8537,73 +8570,6 @@
         return handleAppCrashLocked(app);
     }
 
-    private ComponentName getErrorReportReceiver(ProcessRecord app) {
-        // check if error reporting is enabled in secure settings
-        int enabled = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SEND_ACTION_APP_ERROR, 0);
-        if (enabled == 0) {
-            return null;
-        }
-
-        IPackageManager pm = ActivityThread.getPackageManager();
-
-        try {
-            // look for receiver in the installer package
-            String candidate = pm.getInstallerPackageName(app.info.packageName);
-            ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate);
-            if (result != null) {
-                return result;
-            }
-
-            // if the error app is on the system image, look for system apps
-            // error receiver
-            if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
-                candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
-                result = getErrorReportReceiver(pm, app.info.packageName, candidate);
-                if (result != null) {
-                    return result;
-                }
-            }
-
-            // if there is a default receiver, try that
-            candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
-            return getErrorReportReceiver(pm, app.info.packageName, candidate);
-        } catch (RemoteException e) {
-            // should not happen
-            Log.e(TAG, "error talking to PackageManager", e);
-            return null;
-        }
-    }
-
-    /**
-     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
-     *
-     * @param pm PackageManager isntance
-     * @param errorPackage package which caused the error
-     * @param receiverPackage candidate package to receive the error
-     * @return activity component within receiverPackage which handles
-     * ACTION_APP_ERROR, or null if not found
-     */
-    private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage,
-            String receiverPackage) throws RemoteException {
-        if (receiverPackage == null || receiverPackage.length() == 0) {
-            return null;
-        }
-
-        // break the loop if it's the error report receiver package that crashed
-        if (receiverPackage.equals(errorPackage)) {
-            return null;
-        }
-
-        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
-        intent.setPackage(receiverPackage);
-        ResolveInfo info = pm.resolveIntent(intent, null, 0);
-        if (info == null || info.activityInfo == null) {
-            return null;
-        }
-        return new ComponentName(receiverPackage, info.activityInfo.name);
-    }
-
     private void makeAppNotRespondingLocked(ProcessRecord app,
             String activity, String shortMsg, String longMsg) {
         app.notResponding = true;
@@ -8717,7 +8683,8 @@
     }
 
     void startAppProblemLocked(ProcessRecord app) {
-        app.errorReportReceiver = getErrorReportReceiver(app);
+        app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+                mContext, app.info.packageName, app.info.flags);
         skipCurrentReceiverLocked(app);
     }
 
@@ -11951,7 +11918,7 @@
                         String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                         if (list != null && (list.length > 0)) {
                             for (String pkg : list) {
-                                forceStopPackageLocked(pkg, -1, false, true);
+                                forceStopPackageLocked(pkg, -1, false, true, true);
                             }
                         }
                     } else {
@@ -11960,7 +11927,7 @@
                         if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
                             if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
                                 forceStopPackageLocked(ssp,
-                                        intent.getIntExtra(Intent.EXTRA_UID, -1), false, true);
+                                        intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true);
                             }
                         }
                     }
@@ -12931,7 +12898,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            forceStopPackageLocked(ii.targetPackage, -1, true, false);
+            forceStopPackageLocked(ii.targetPackage, -1, true, false, true);
             ProcessRecord app = addAppLocked(ai);
             app.instrumentationClass = className;
             app.instrumentationInfo = ai;
@@ -12986,7 +12953,7 @@
         app.instrumentationProfileFile = null;
         app.instrumentationArguments = null;
 
-        forceStopPackageLocked(app.processName, -1, false, false);
+        forceStopPackageLocked(app.processName, -1, false, false, true);
     }
 
     public void finishInstrumentation(IApplicationThread target,