auto import from //branches/cupcake/...@131421
diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java
index 5ef0fb9..ddf3afe 100644
--- a/services/java/com/android/server/GadgetService.java
+++ b/services/java/com/android/server/GadgetService.java
@@ -31,9 +31,10 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.gadget.GadgetManager;
-import android.gadget.GadgetInfo;
+import android.gadget.GadgetProviderInfo;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -78,7 +79,7 @@
 
     static class Provider {
         int uid;
-        GadgetInfo info;
+        GadgetProviderInfo info;
         ArrayList<GadgetId> instances = new ArrayList();
         PendingIntent broadcast;
         
@@ -106,7 +107,7 @@
     PackageManager mPackageManager;
     AlarmManager mAlarmManager;
     ArrayList<Provider> mInstalledProviders = new ArrayList();
-    int mNextGadgetId = 1;
+    int mNextGadgetId = GadgetManager.INVALID_GADGET_ID + 1;
     ArrayList<GadgetId> mGadgetIds = new ArrayList();
     ArrayList<Host> mHosts = new ArrayList();
 
@@ -128,7 +129,6 @@
         // update the provider list.
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mBroadcastReceiver, filter);
@@ -148,7 +148,7 @@
             int N = mInstalledProviders.size();
             pw.println("Providers: (size=" + N + ")");
             for (int i=0; i<N; i++) {
-                GadgetInfo info = mInstalledProviders.get(i).info;
+                GadgetProviderInfo info = mInstalledProviders.get(i).info;
                 pw.println("  [" + i + "] provder=" + info.provider
                         + " min=(" + info.minWidth + "x" + info.minHeight + ")"
                         + " updatePeriodMillis=" + info.updatePeriodMillis
@@ -265,7 +265,7 @@
         if (p != null) {
             p.instances.remove(id);
             // send the broacast saying that this gadgetId has been deleted
-            Intent intent = new Intent(GadgetManager.GADGET_DELETED_ACTION);
+            Intent intent = new Intent(GadgetManager.ACTION_GADGET_DELETED);
             intent.setComponent(p.info.provider);
             intent.putExtra(GadgetManager.EXTRA_GADGET_ID, id.gadgetId);
             mContext.sendBroadcast(intent);
@@ -274,7 +274,7 @@
                 cancelBroadcasts(p);
 
                 // send the broacast saying that the provider is not in use any more
-                intent = new Intent(GadgetManager.GADGET_DISABLED_ACTION);
+                intent = new Intent(GadgetManager.ACTION_GADGET_DISABLED);
                 intent.setComponent(p.info.provider);
                 mContext.sendBroadcast(intent);
             }
@@ -331,7 +331,7 @@
         }
     }
 
-    public GadgetInfo getGadgetInfo(int gadgetId) {
+    public GadgetProviderInfo getGadgetInfo(int gadgetId) {
         synchronized (mGadgetIds) {
             GadgetId id = lookupGadgetIdLocked(gadgetId);
             if (id != null) {
@@ -351,10 +351,10 @@
         }
     }
 
-    public List<GadgetInfo> getInstalledProviders() {
+    public List<GadgetProviderInfo> getInstalledProviders() {
         synchronized (mGadgetIds) {
             final int N = mInstalledProviders.size();
-            ArrayList<GadgetInfo> result = new ArrayList(N);
+            ArrayList<GadgetProviderInfo> result = new ArrayList(N);
             for (int i=0; i<N; i++) {
                 result.add(mInstalledProviders.get(i).info);
             }
@@ -364,7 +364,7 @@
 
     public void updateGadgetIds(int[] gadgetIds, RemoteViews views) {
         if (gadgetIds == null) {
-            throw new IllegalArgumentException("bad gadgetIds");
+            return;
         }
         if (gadgetIds.length == 0) {
             return;
@@ -408,7 +408,7 @@
                     // the lock is held, but this is a oneway call
                     id.host.callbacks.updateGadget(id.gadgetId, views);
                 } catch (RemoteException e) {
-                    // It failed, remove the callback. No need to prune because
+                    // It failed; remove the callback. No need to prune because
                     // we know that this host is still referenced by this instance.
                     id.host.callbacks = null;
                 }
@@ -524,10 +524,7 @@
     void getGadgetList() {
         PackageManager pm = mPackageManager;
 
-        // TODO: We have these as different actions.  I wonder if it makes more sense to
-        // have like a GADGET_ACTION, and then subcommands.  It's kind of arbitrary that
-        // we look for GADGET_UPDATE_ACTION and not any of the other gadget actions.
-        Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+        Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE);
         List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
 
@@ -538,11 +535,14 @@
         }
     }
 
-    void addProviderLocked(ResolveInfo ri) {
-        Provider p = parseGadgetInfoXml(new ComponentName(ri.activityInfo.packageName,
+    boolean addProviderLocked(ResolveInfo ri) {
+        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
                     ri.activityInfo.name), ri);
         if (p != null) {
             mInstalledProviders.add(p);
+            return true;
+        } else {
+            return false;
         }
     }
 
@@ -568,16 +568,18 @@
     }
 
     void sendEnableIntentLocked(Provider p) {
-        Intent intent = new Intent(GadgetManager.GADGET_ENABLED_ACTION);
+        Intent intent = new Intent(GadgetManager.ACTION_GADGET_ENABLED);
         intent.setComponent(p.info.provider);
         mContext.sendBroadcast(intent);
     }
 
     void sendUpdateIntentLocked(Provider p, int[] gadgetIds) {
-        Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
-        intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds);
-        intent.setComponent(p.info.provider);
-        mContext.sendBroadcast(intent);
+        if (gadgetIds != null && gadgetIds.length > 0) {
+            Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE);
+            intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds);
+            intent.setComponent(p.info.provider);
+            mContext.sendBroadcast(intent);
+        }
     }
 
     void registerForBroadcastsLocked(Provider p, int[] gadgetIds) {
@@ -587,7 +589,7 @@
             // PendingIntent.getBroadcast will update the extras.
             boolean alreadyRegistered = p.broadcast != null;
             int instancesSize = p.instances.size();
-            Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+            Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE);
             intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds);
             intent.setComponent(p.info.provider);
             long token = Binder.clearCallingIdentity();
@@ -614,16 +616,16 @@
         return gadgetIds;
     }
 
-    private Provider parseGadgetInfoXml(ComponentName component, ResolveInfo ri) {
+    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
         Provider p = null;
 
         ActivityInfo activityInfo = ri.activityInfo;
         XmlResourceParser parser = null;
         try {
             parser = activityInfo.loadXmlMetaData(mPackageManager,
-                    GadgetManager.GADGET_PROVIDER_META_DATA);
+                    GadgetManager.META_DATA_GADGET_PROVIDER);
             if (parser == null) {
-                Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for "
+                Log.w(TAG, "No " + GadgetManager.META_DATA_GADGET_PROVIDER + " meta-data for "
                         + "gadget provider '" + component + '\'');
                 return null;
             }
@@ -644,7 +646,7 @@
             }
 
             p = new Provider();
-            GadgetInfo info = p.info = new GadgetInfo();
+            GadgetProviderInfo info = p.info = new GadgetProviderInfo();
 
             info.provider = component;
             p.uid = activityInfo.applicationInfo.uid;
@@ -672,6 +674,7 @@
             // of what a client process passes to us should not be fatal for the
             // system process.
             Log.w(TAG, "XML parsing failed for gadget provider '" + component + '\'', e);
+            return null;
         } finally {
             if (parser != null) parser.close();
         }
@@ -979,20 +982,26 @@
                 
                 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
                     synchronized (mGadgetIds) {
-                        addProvidersForPackageLocked(pkgName);
-                        saveStateLocked();
-                    }
-                }
-                else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                    synchronized (mGadgetIds) {
-                        updateProvidersForPackageLocked(pkgName);
+                        Bundle extras = intent.getExtras();
+                        if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                            // The package was just upgraded
+                            updateProvidersForPackageLocked(pkgName);
+                        } else {
+                            // The package was just added
+                            addProvidersForPackageLocked(pkgName);
+                        }
                         saveStateLocked();
                     }
                 }
                 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    synchronized (mGadgetIds) {
-                        removeProvidersForPackageLocked(pkgName);
-                        saveStateLocked();
+                    Bundle extras = intent.getExtras();
+                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                        // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
+                    } else {
+                        synchronized (mGadgetIds) {
+                            removeProvidersForPackageLocked(pkgName);
+                            saveStateLocked();
+                        }
                     }
                 }
             }
@@ -1002,7 +1011,7 @@
     // TODO: If there's a better way of matching an intent filter against the
     // packages for a given package, use that.
     void addProvidersForPackageLocked(String pkgName) {
-        Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+        Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE);
         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
 
@@ -1021,7 +1030,7 @@
     // packages for a given package, use that.
     void updateProvidersForPackageLocked(String pkgName) {
         HashSet<String> keep = new HashSet();
-        Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+        Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE);
         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
 
@@ -1031,17 +1040,53 @@
             ResolveInfo ri = broadcastReceivers.get(i);
             ActivityInfo ai = ri.activityInfo;
             if (pkgName.equals(ai.packageName)) {
-                Provider p = lookupProviderLocked(new ComponentName(ai.packageName, ai.name));
+                ComponentName component = new ComponentName(ai.packageName, ai.name);
+                Provider p = lookupProviderLocked(component);
                 if (p == null) {
-                    addProviderLocked(ri);
+                    if (addProviderLocked(ri)) {
+                        keep.add(ai.name);
+                    }
+                } else {
+                    Provider parsed = parseProviderInfoXml(component, ri);
+                    if (parsed != null) {
+                        keep.add(ai.name);
+                        // Use the new GadgetProviderInfo.
+                        GadgetProviderInfo oldInfo = p.info;
+                        p.info = parsed.info;
+                        // If it's enabled
+                        final int M = p.instances.size();
+                        if (M > 0) {
+                            int[] gadgetIds = getGadgetIds(p);
+                            // Reschedule for the new updatePeriodMillis (don't worry about handling
+                            // it specially if updatePeriodMillis didn't change because we just sent
+                            // an update, and the next one will be updatePeriodMillis from now).
+                            cancelBroadcasts(p);
+                            registerForBroadcastsLocked(p, gadgetIds);
+                            // If it's currently showing, call back with the new GadgetProviderInfo.
+                            for (int j=0; j<M; j++) {
+                                GadgetId id = p.instances.get(j);
+                                if (id.host != null && id.host.callbacks != null) {
+                                    try {
+                                        id.host.callbacks.providerChanged(id.gadgetId, p.info);
+                                    } catch (RemoteException ex) {
+                                        // It failed; remove the callback. No need to prune because
+                                        // we know that this host is still referenced by this
+                                        // instance.
+                                        id.host.callbacks = null;
+                                    }
+                                }
+                            }
+                            // Now that we've told the host, push out an update.
+                            sendUpdateIntentLocked(p, gadgetIds);
+                        }
+                    }
                 }
-                keep.add(ai.name);
             }
         }
 
         // prune the ones we don't want to keep
         N = mInstalledProviders.size();
-        for (int i=0; i<N; i++) {
+        for (int i=N-1; i>=0; i--) {
             Provider p = mInstalledProviders.get(i);
             if (pkgName.equals(p.info.provider.getPackageName())
                     && !keep.contains(p.info.provider.getClassName())) {
@@ -1052,7 +1097,7 @@
 
     void removeProvidersForPackageLocked(String pkgName) {
         int N = mInstalledProviders.size();
-        for (int i=0; i<N; i++) {
+        for (int i=N-1; i>=0; i--) {
             Provider p = mInstalledProviders.get(i);
             if (pkgName.equals(p.info.provider.getPackageName())) {
                 removeProviderLocked(i, p);
@@ -1064,7 +1109,7 @@
         // By now, we have removed any gadgets that were in any hosts here,
         // so we don't need to worry about sending DISABLE broadcasts to them.
         N = mHosts.size();
-        for (int i=0; i<N; i++) {
+        for (int i=N-1; i>=0; i--) {
             Host host = mHosts.get(i);
             if (pkgName.equals(host.packageName)) {
                 deleteHostLocked(host);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index ee49365..7588129 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -55,8 +55,10 @@
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.EventLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -98,6 +100,10 @@
     static final int MSG_UNBIND_METHOD = 3000;
     static final int MSG_BIND_METHOD = 3010;
     
+    static final long TIME_TO_RECONNECT = 10*1000;
+    
+    static final int LOG_IMF_FORCE_RECONNECT_IME = 32000;
+    
     final Context mContext;
     final Handler mHandler;
     final SettingsObserver mSettingsObserver;
@@ -248,6 +254,12 @@
     IInputMethod mCurMethod;
     
     /**
+     * Time that we last initiated a bind to the input method, to determine
+     * if we should try to disconnect and reconnect to it.
+     */
+    long mLastBindTime;
+    
+    /**
      * Have we called mCurMethod.bindInput()?
      */
     boolean mBoundToMethod;
@@ -486,7 +498,6 @@
     }
 
     public void systemReady() {
-        
     }
     
     public List<InputMethodInfo> getInputMethodList() {
@@ -571,7 +582,7 @@
         }
     }
     
-    private int getShowFlags() {
+    private int getImeShowFlags() {
         int flags = 0;
         if (mShowForced) {
             flags |= InputMethod.SHOW_FORCED
@@ -582,6 +593,16 @@
         return flags;
     }
     
+    private int getAppShowFlags() {
+        int flags = 0;
+        if (mShowForced) {
+            flags |= InputMethodManager.SHOW_FORCED;
+        } else if (!mShowExplicitlyRequested) {
+            flags |= InputMethodManager.SHOW_IMPLICIT;
+        }
+        return flags;
+    }
+    
     InputBindResult attachNewInputLocked(boolean initial, boolean needResult) {
         if (!mBoundToMethod) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
@@ -598,7 +619,7 @@
         }
         if (mShowRequested) {
             if (DEBUG) Log.v(TAG, "Attach new input asks to show input");
-            showCurrentInputLocked(getShowFlags());
+            showCurrentInputLocked(getAppShowFlags());
         }
         return needResult
                 ? new InputBindResult(session.session, mCurId, mCurSeq)
@@ -666,14 +687,31 @@
                 return attachNewInputLocked(initial, needResult);
             }
             if (mHaveConnection) {
-                if (mCurMethod != null && !cs.sessionRequested) {
-                    cs.sessionRequested = true;
-                    if (DEBUG) Log.v(TAG, "Creating new session for client " + cs);
-                    executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
-                            MSG_CREATE_SESSION, mCurMethod,
-                            new MethodCallback(mCurMethod)));
+                if (mCurMethod != null) {
+                    if (!cs.sessionRequested) {
+                        cs.sessionRequested = true;
+                        if (DEBUG) Log.v(TAG, "Creating new session for client " + cs);
+                        executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
+                                MSG_CREATE_SESSION, mCurMethod,
+                                new MethodCallback(mCurMethod)));
+                    }
+                    // Return to client, and we will get back with it when
+                    // we have had a session made for it.
+                    return new InputBindResult(null, mCurId, mCurSeq);
+                } else if (SystemClock.uptimeMillis()
+                        < (mLastBindTime+TIME_TO_RECONNECT)) {
+                    // In this case we have connected to the service, but
+                    // don't yet have its interface.  If it hasn't been too
+                    // long since we did the connection, we'll return to
+                    // the client and wait to get the service interface so
+                    // we can report back.  If it has been too long, we want
+                    // to fall through so we can try a disconnect/reconnect
+                    // to see if we can get back in touch with the service.
+                    return new InputBindResult(null, mCurId, mCurSeq);
+                } else {
+                    EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId,
+                            SystemClock.uptimeMillis()-mLastBindTime, 0);
                 }
-                return new InputBindResult(null, mCurId, mCurSeq);
             }
         }
         
@@ -682,6 +720,11 @@
             throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
         }
         
+        if (mHaveConnection) {
+            mContext.unbindService(this);
+            mHaveConnection = false;
+        }
+        
         if (mCurToken != null) {
             try {
                 if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
@@ -691,16 +734,12 @@
             mCurToken = null;
         }
         
-        if (mHaveConnection) {
-            mContext.unbindService(this);
-            mHaveConnection = false;
-        }
-        
         clearCurMethod();
         
         mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
         mCurIntent.setComponent(info.getComponent());
         if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
+            mLastBindTime = SystemClock.uptimeMillis();
             mHaveConnection = true;
             mCurId = info.getId();
             mCurToken = new Binder();
@@ -758,7 +797,8 @@
 
     void onSessionCreated(IInputMethod method, IInputMethodSession session) {
         synchronized (mMethodMap) {
-            if (mCurMethod == method) {
+            if (mCurMethod != null && method != null
+                    && mCurMethod.asBinder() == method.asBinder()) {
                 if (mCurClient != null) {
                     mCurClient.curSession = new SessionState(mCurClient,
                             method, session);
@@ -781,6 +821,7 @@
             }
             mCurMethod = null;
         }
+        mStatusBar.setIconVisibility(mInputMethodIcon, false);
     }
     
     public void onServiceDisconnected(ComponentName name) {
@@ -790,6 +831,9 @@
             if (mCurMethod != null && mCurIntent != null
                     && name.equals(mCurIntent.getComponent())) {
                 clearCurMethod();
+                // We consider this to be a new bind attempt, since the system
+                // should now try to restart the service for us.
+                mLastBindTime = SystemClock.uptimeMillis();
                 mShowRequested = mInputShown;
                 mInputShown = false;
                 if (mCurClient != null) {
@@ -800,23 +844,28 @@
         }
     }
 
-    public void updateStatusIcon(int iconId, String iconPackage) {
-        if (iconId == 0) {
-            Log.d(TAG, "hide the small icon for the input method");
-            mStatusBar.setIconVisibility(mInputMethodIcon, false);
-        } else {
-            Log.d(TAG, "show a small icon for the input method");
-
-            if (iconPackage != null
-                    && iconPackage
-                            .equals(InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) {
-                iconPackage = null;
+    public void updateStatusIcon(IBinder token, String packageName, int iconId) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            if (token == null || mCurToken != token) {
+                Log.w(TAG, "Ignoring setInputMethod of token: " + token);
+                return;
             }
-
-            mInputMethodData.iconId = iconId;
-            mInputMethodData.iconPackage = iconPackage;
-            mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
-            mStatusBar.setIconVisibility(mInputMethodIcon, true);
+            
+            synchronized (mMethodMap) {
+                if (iconId == 0) {
+                    if (DEBUG) Log.d(TAG, "hide the small icon for the input method");
+                    mStatusBar.setIconVisibility(mInputMethodIcon, false);
+                } else if (packageName != null) {
+                    if (DEBUG) Log.d(TAG, "show a small icon for the input method");
+                    mInputMethodData.iconId = iconId;
+                    mInputMethodData.iconPackage = packageName;
+                    mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
+                    mStatusBar.setIconVisibility(mInputMethodIcon, true);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -860,23 +909,28 @@
     }
     
     public void showSoftInput(IInputMethodClient client, int flags) {
-        synchronized (mMethodMap) {
-            if (mCurClient == null || client == null
-                    || mCurClient.client.asBinder() != client.asBinder()) {
-                try {
-                    // We need to check if this is the current client with
-                    // focus in the window manager, to allow this call to
-                    // be made before input is started in it.
-                    if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                        Log.w(TAG, "Ignoring showSoftInput of: " + client);
-                        return;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mMethodMap) {
+                if (mCurClient == null || client == null
+                        || mCurClient.client.asBinder() != client.asBinder()) {
+                    try {
+                        // We need to check if this is the current client with
+                        // focus in the window manager, to allow this call to
+                        // be made before input is started in it.
+                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
+                            Log.w(TAG, "Ignoring showSoftInput of: " + client);
+                            return;
+                        }
+                    } catch (RemoteException e) {
                     }
-                } catch (RemoteException e) {
                 }
+    
+                if (DEBUG) Log.v(TAG, "Client requesting input be shown");
+                showCurrentInputLocked(flags);
             }
-
-            if (DEBUG) Log.v(TAG, "Client requesting input be shown");
-            showCurrentInputLocked(flags);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
     
@@ -891,29 +945,44 @@
         }
         if (mCurMethod != null) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO(
-                    MSG_SHOW_SOFT_INPUT, getShowFlags(), mCurMethod));
+                    MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod));
             mInputShown = true;
+        } else if (mHaveConnection && SystemClock.uptimeMillis()
+                < (mLastBindTime+TIME_TO_RECONNECT)) {
+            // The client has asked to have the input method shown, but
+            // we have been sitting here too long with a connection to the
+            // service and no interface received, so let's disconnect/connect
+            // to try to prod things along.
+            EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId,
+                    SystemClock.uptimeMillis()-mLastBindTime,1);
+            mContext.unbindService(this);
+            mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE);
         }
     }
     
     public void hideSoftInput(IInputMethodClient client, int flags) {
-        synchronized (mMethodMap) {
-            if (mCurClient == null || client == null
-                    || mCurClient.client.asBinder() != client.asBinder()) {
-                try {
-                    // We need to check if this is the current client with
-                    // focus in the window manager, to allow this call to
-                    // be made before input is started in it.
-                    if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                        Log.w(TAG, "Ignoring hideSoftInput of: " + client);
-                        return;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mMethodMap) {
+                if (mCurClient == null || client == null
+                        || mCurClient.client.asBinder() != client.asBinder()) {
+                    try {
+                        // We need to check if this is the current client with
+                        // focus in the window manager, to allow this call to
+                        // be made before input is started in it.
+                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
+                            Log.w(TAG, "Ignoring hideSoftInput of: " + client);
+                            return;
+                        }
+                    } catch (RemoteException e) {
                     }
-                } catch (RemoteException e) {
                 }
+    
+                if (DEBUG) Log.v(TAG, "Client requesting input be hidden");
+                hideCurrentInputLocked(flags);
             }
-
-            if (DEBUG) Log.v(TAG, "Client requesting input be hidden");
-            hideCurrentInputLocked(flags);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
     
@@ -942,70 +1011,75 @@
     public void windowGainedFocus(IInputMethodClient client,
             boolean viewHasFocus, boolean isTextEditor, int softInputMode,
             boolean first, int windowFlags) {
-        synchronized (mMethodMap) {
-            if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder()
-                    + " viewHasFocus=" + viewHasFocus
-                    + " isTextEditor=" + isTextEditor
-                    + " softInputMode=#" + Integer.toHexString(softInputMode)
-                    + " first=" + first + " flags=#"
-                    + Integer.toHexString(windowFlags));
-            
-            if (mCurClient == null || client == null
-                    || mCurClient.client.asBinder() != client.asBinder()) {
-                try {
-                    // We need to check if this is the current client with
-                    // focus in the window manager, to allow this call to
-                    // be made before input is started in it.
-                    if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                        Log.w(TAG, "Ignoring focus gain of: " + client);
-                        return;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mMethodMap) {
+                if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder()
+                        + " viewHasFocus=" + viewHasFocus
+                        + " isTextEditor=" + isTextEditor
+                        + " softInputMode=#" + Integer.toHexString(softInputMode)
+                        + " first=" + first + " flags=#"
+                        + Integer.toHexString(windowFlags));
+                
+                if (mCurClient == null || client == null
+                        || mCurClient.client.asBinder() != client.asBinder()) {
+                    try {
+                        // We need to check if this is the current client with
+                        // focus in the window manager, to allow this call to
+                        // be made before input is started in it.
+                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
+                            Log.w(TAG, "Ignoring focus gain of: " + client);
+                            return;
+                        }
+                    } catch (RemoteException e) {
                     }
-                } catch (RemoteException e) {
+                }
+    
+                switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
+                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
+                        if (!isTextEditor || (softInputMode &
+                                WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
+                                != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
+                            if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
+                                // There is no focus view, and this window will
+                                // be behind any soft input window, so hide the
+                                // soft input window if it is shown.
+                                if (DEBUG) Log.v(TAG, "Unspecified window will hide input");
+                                hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS);
+                            }
+                        } else if (isTextEditor && (softInputMode &
+                                WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
+                                == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+                                && (softInputMode &
+                                        WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
+                            // There is a focus view, and we are navigating forward
+                            // into the window, so show the input window for the user.
+                            if (DEBUG) Log.v(TAG, "Unspecified window will show input");
+                            showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT);
+                        }
+                        break;
+                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+                        // Do nothing.
+                        break;
+                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+                        if (DEBUG) Log.v(TAG, "Window asks to hide input");
+                        hideCurrentInputLocked(0);
+                        break;
+                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+                        if ((softInputMode &
+                                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
+                            if (DEBUG) Log.v(TAG, "Window asks to show input going forward");
+                            showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT);
+                        }
+                        break;
+                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+                        if (DEBUG) Log.v(TAG, "Window asks to always show input");
+                        showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT);
+                        break;
                 }
             }
-
-            switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
-                case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
-                    if (!isTextEditor || (softInputMode &
-                            WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
-                            != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
-                        if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
-                            // There is no focus view, and this window will
-                            // be behind any soft input window, so hide the
-                            // soft input window if it is shown.
-                            if (DEBUG) Log.v(TAG, "Unspecified window will hide input");
-                            hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS);
-                        }
-                    } else if (isTextEditor && (softInputMode &
-                            WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
-                            == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
-                            && (softInputMode &
-                                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
-                        // There is a focus view, and we are navigating forward
-                        // into the window, so show the input window for the user.
-                        if (DEBUG) Log.v(TAG, "Unspecified window will show input");
-                        showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT);
-                    }
-                    break;
-                case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
-                    // Do nothing.
-                    break;
-                case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
-                    if (DEBUG) Log.v(TAG, "Window asks to hide input");
-                    hideCurrentInputLocked(0);
-                    break;
-                case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
-                    if ((softInputMode &
-                            WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
-                        if (DEBUG) Log.v(TAG, "Window asks to show input going forward");
-                        showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT);
-                    }
-                    break;
-                case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                    if (DEBUG) Log.v(TAG, "Window asks to always show input");
-                    showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT);
-                    break;
-            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
     
@@ -1022,7 +1096,7 @@
 
     public void setInputMethod(IBinder token, String id) {
         synchronized (mMethodMap) {
-            if (mCurToken == null) {
+            if (token == null) {
                 if (mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.WRITE_SECURE_SETTINGS)
                         != PackageManager.PERMISSION_GRANTED) {
@@ -1032,19 +1106,31 @@
                 }
             } else if (mCurToken != token) {
                 Log.w(TAG, "Ignoring setInputMethod of token: " + token);
+                return;
             }
 
-            setInputMethodLocked(id);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                setInputMethodLocked(id);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
     }
 
     public void hideMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
-            if (mCurToken == null || mCurToken != token) {
+            if (token == null || mCurToken != token) {
                 Log.w(TAG, "Ignoring hideInputMethod of token: " + token);
+                return;
             }
 
-            hideCurrentInputLocked(flags);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                hideCurrentInputLocked(flags);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
     }
 
@@ -1209,7 +1295,7 @@
     
     // ----------------------------------------------------------------------
     
-    public void showInputMethodMenu() {
+    void showInputMethodMenu() {
         if (DEBUG) Log.v(TAG, "Show switching menu");
 
         hideInputMethodMenu();
@@ -1309,83 +1395,88 @@
                         + android.Manifest.permission.WRITE_SECURE_SETTINGS);
             }
             
-            // Make sure this is a valid input method.
-            InputMethodInfo imm = mMethodMap.get(id);
-            if (imm == null) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                // Make sure this is a valid input method.
+                InputMethodInfo imm = mMethodMap.get(id);
                 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 (imm == null) {
+                        throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
                     }
                 }
-            }
-            
-            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;
+                
+                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);
+                        }
+                    }
                 }
-                // 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)) {
+                
+                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.DEFAULT_INPUT_METHOD,
-                            firstId != null ? firstId : "");
+                            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;
                 }
-                // 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;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-            
-            // 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 bc6fd71..db4daa5 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -132,7 +132,7 @@
     private static final int MESSAGE_HEARTBEAT = 1;
     private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
     private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
-    private static final int MESSAGE_SET_NETWORK_LOCATION_PROVIDER = 4;
+    private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4;
 
     // Alarm manager and wakelock variables
     private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
@@ -565,13 +565,21 @@
         }
     }
 
-    public void setNetworkLocationProvider(INetworkLocationProvider provider) {
-        mLocationHandler.removeMessages(MESSAGE_SET_NETWORK_LOCATION_PROVIDER);
+    public void setInstallCallback(InstallCallback callback) {
+        mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER);
         Message m = Message.obtain(mLocationHandler, 
-                MESSAGE_SET_NETWORK_LOCATION_PROVIDER, provider);
+                MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback);
         mLocationHandler.sendMessageAtFrontOfQueue(m);
     }
 
+    public void setNetworkLocationProvider(INetworkLocationProvider provider) {
+        mNetworkLocationInterface = provider;
+        provider.addListener(getPackageNames());
+        mNetworkLocationProvider = (LocationProviderImpl)provider;
+        LocationProviderImpl.addProvider(mNetworkLocationProvider);
+        updateProviders();
+    }
+
     public void setLocationCollector(ILocationCollector collector) {
         mCollector = collector;
         if (mGpsLocationProvider != null) {
@@ -1598,16 +1606,12 @@
                     // Update wakelock status so the next alarm is set before releasing wakelock
                     updateWakelockStatus(mScreenOn);
                     releaseWakeLock();
-                } else if (msg.what == MESSAGE_SET_NETWORK_LOCATION_PROVIDER) {
+                } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) {
                     synchronized (LocationManagerService.class) {
-                        Log.d(TAG, "adding network location provider");
-                        mNetworkLocationInterface = 
-                                (INetworkLocationProvider)msg.obj;
-                        mNetworkLocationInterface.addListener(getPackageNames());
-                        mNetworkLocationProvider = 
-                                (LocationProviderImpl)mNetworkLocationInterface;
-                        LocationProviderImpl.addProvider(mNetworkLocationProvider);
-                        updateProviders();
+                        Log.d(TAG, "installing network location provider");
+                        INetworkLocationManager.InstallCallback callback =
+                                (INetworkLocationManager.InstallCallback)msg.obj;
+                        callback.installNetworkLocationProvider(mContext, LocationManagerService.this);
                     }
                 }
             } catch (Exception e) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 221ba46..c490e42 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3219,12 +3219,15 @@
                         Log.i(TAG, "Observer no longer exists.");
                     }
                 }
-                // There appears to be a subtle deadlock condition if the sendPackageBroadcast call appears
-                // in the synchronized block above.
+                // There appears to be a subtle deadlock condition if the sendPackageBroadcast
+                // call appears in the synchronized block above.
                 if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                     res.removedInfo.sendBroadcast(false, true);
                     Bundle extras = new Bundle(1);
                     extras.putInt(Intent.EXTRA_UID, res.uid);
+                    if (res.removedInfo.removedPackage != null) {
+                        extras.putBoolean(Intent.EXTRA_REPLACING, true);
+                    }
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                          res.pkg.applicationInfo.packageName,
                                          extras);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 7c111d3..6974b5e 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1001,11 +1001,9 @@
                 }
             }
             else {
-                synchronized (mLocks) {
-                    EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 4,
-                            mBroadcastWakeLock.mCount);
-                    mBroadcastWakeLock.release();
-                }
+                // If we're in this case, then this handler is running for a previous
+                // paired transaction.  mBroadcastWakeLock will already have been released
+                // in sendNotificationLocked.
             }
         }
     };
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index c009fac..eece581 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -87,10 +87,13 @@
 
     private final LockList mLocks = new LockList();
     /**
-     * See {@link Settings.System#WIFI_IDLE_MS}. This is the default value if a
-     * Settings.System value is not present.
+     * See {@link Settings.Gservices#WIFI_IDLE_MS}. This is the default value if a
+     * Settings.Gservices value is not present. This timeout value is chosen as
+     * the approximate point at which the battery drain caused by Wi-Fi
+     * being enabled but not active exceeds the battery drain caused by
+     * re-establishing a connection to the mobile data network.
      */
-    private static final long DEFAULT_IDLE_MILLIS = 2 * 60 * 1000; /* 2 minutes */
+    private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
 
     private static final String WAKELOCK_TAG = "WifiService";
 
@@ -101,7 +104,7 @@
      * provides a bit of extra margin.
      * <p>
      * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
-     * This is the default value if a Settings.System value is not present.
+     * This is the default value if a Settings.Secure value is not present.
      */
     private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
 
@@ -1364,7 +1367,7 @@
      * in the current regulatory domain. This method should be used only
      * if the correct number of channels cannot be determined automatically
      * for some reason. If the operation is successful, the new value is
-     * persisted as a System setting.
+     * persisted as a Secure setting.
      * @param numChannels the number of allowed channels. Must be greater than 0
      * and less than or equal to 16.
      * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
@@ -1446,8 +1449,8 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
-            long idleMillis = Settings.System.getLong(mContext.getContentResolver(),
-                                                  Settings.System.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
+            long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(),
+                                                  Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
             int stayAwakeConditions =
                     Settings.System.getInt(mContext.getContentResolver(),
                                            Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 09f5d8f..fed6d12 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -768,27 +768,55 @@
         if (willMove && w != null) {
             final WindowState curTarget = mInputMethodTarget;
             if (curTarget != null && curTarget.mAppToken != null) {
-                int curIndex = -1;
-                if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" 
-                        + mNextAppTransition + " curTarget animating="
-                        + curTarget.isAnimating()
-                        + " layer=" + curTarget.mAnimLayer
-                        + " new layer=" + w.mAnimLayer);
-                if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
-                    // If we are currently setting up for an animation,
-                    // hold everything until we can find out what will happen.
-                    mInputMethodTargetWaitingAnim = true;
-                    curIndex = localmWindows.indexOf(curTarget);
-                } else if (curTarget.isAnimating() &&
-                        curTarget.mAnimLayer > w.mAnimLayer) {
-                    // If the window we are currently targeting is involved
-                    // with an animation, and it is on top of the next target
-                    // we will be over, then hold off on moving until
-                    // that is done.
-                    curIndex = localmWindows.indexOf(curTarget);
+                
+                // Now some fun for dealing with window animations that
+                // modify the Z order.  We need to look at all windows below
+                // the current target that are in this app, finding the highest
+                // visible one in layering.
+                AppWindowToken token = curTarget.mAppToken;
+                WindowState highestTarget = null;
+                int highestPos = 0;
+                if (token.animating || token.animation != null) {
+                    int pos = 0;
+                    pos = localmWindows.indexOf(curTarget);
+                    while (pos >= 0) {
+                        WindowState win = (WindowState)localmWindows.get(pos);
+                        if (win.mAppToken != token) {
+                            break;
+                        }
+                        if (!win.mRemoved) {
+                            if (highestTarget == null || win.mAnimLayer >
+                                    highestTarget.mAnimLayer) {
+                                highestTarget = win;
+                                highestPos = pos;
+                            }
+                        }
+                        pos--;
+                    }
                 }
-                if (curIndex >= 0) {
-                    return curIndex + 1;
+                
+                if (highestTarget != null) {
+                    if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" 
+                            + mNextAppTransition + " " + highestTarget
+                            + " animating=" + highestTarget.isAnimating()
+                            + " layer=" + highestTarget.mAnimLayer
+                            + " new layer=" + w.mAnimLayer);
+                    
+                    if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+                        // If we are currently setting up for an animation,
+                        // hold everything until we can find out what will happen.
+                        mInputMethodTargetWaitingAnim = true;
+                        mInputMethodTarget = highestTarget;
+                        return highestPos + 1;
+                    } else if (highestTarget.isAnimating() &&
+                            highestTarget.mAnimLayer > w.mAnimLayer) {
+                        // If the window we are currently targeting is involved
+                        // with an animation, and it is on top of the next target
+                        // we will be over, then hold off on moving until
+                        // that is done.
+                        mInputMethodTarget = highestTarget;
+                        return highestPos + 1;
+                    }
                 }
             }
         }
@@ -891,12 +919,27 @@
         }
     }
     
+    void logWindowList(String prefix) {
+        int N = mWindows.size();
+        while (N > 0) {
+            N--;
+            Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N));
+        }
+    }
+    
     void moveInputMethodDialogsLocked(int pos) {
         ArrayList<WindowState> dialogs = mInputMethodDialogs;
+        
         final int N = dialogs.size();
+        if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos);
         for (int i=0; i<N; i++) {
             pos = tmpRemoveWindowLocked(pos, dialogs.get(i));
         }
+        if (DEBUG_INPUT_METHOD) {
+            Log.v(TAG, "Window list w/pos=" + pos);
+            logWindowList("  ");
+        }
+        
         if (pos >= 0) {
             final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
             if (pos < mWindows.size()) {
@@ -905,17 +948,26 @@
                     pos++;
                 }
             }
+            if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos);
             for (int i=0; i<N; i++) {
                 WindowState win = dialogs.get(i);
                 win.mTargetAppToken = targetAppToken;
                 pos = reAddWindowLocked(pos, win);
             }
+            if (DEBUG_INPUT_METHOD) {
+                Log.v(TAG, "Final window list:");
+                logWindowList("  ");
+            }
             return;
         }
         for (int i=0; i<N; i++) {
             WindowState win = dialogs.get(i);
             win.mTargetAppToken = null;
             reAddWindowToListInOrderLocked(win);
+            if (DEBUG_INPUT_METHOD) {
+                Log.v(TAG, "No IM target, final list:");
+                logWindowList("  ");
+            }
         }
     }
     
@@ -971,9 +1023,21 @@
             }
             
             if (imWin != null) {
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "Moving IM from " + imPos);
+                    logWindowList("  ");
+                }
                 imPos = tmpRemoveWindowLocked(imPos, imWin);
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "List after moving with new pos " + imPos + ":");
+                    logWindowList("  ");
+                }
                 imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
                 reAddWindowLocked(imPos, imWin);
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "List after moving IM to " + imPos + ":");
+                    logWindowList("  ");
+                }
                 if (DN > 0) moveInputMethodDialogsLocked(imPos+1);
             } else {
                 moveInputMethodDialogsLocked(imPos);
@@ -984,9 +1048,14 @@
             // because they aren't currently associated with a focus window.
             
             if (imWin != null) {
+                if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos);
                 tmpRemoveWindowLocked(0, imWin);
                 imWin.mTargetAppToken = null;
                 reAddWindowToListInOrderLocked(imWin);
+                if (DEBUG_INPUT_METHOD) {
+                    Log.v(TAG, "List with no IM target:");
+                    logWindowList("  ");
+                }
                 if (DN > 0) moveInputMethodDialogsLocked(-1);;
             } else {
                 moveInputMethodDialogsLocked(-1);;
@@ -1233,7 +1302,7 @@
         if (win.mSurface != null && !mDisplayFrozen) {
             // If we are not currently running the exit animation, we
             // need to see about starting one.
-            if (wasVisible=win.isVisibleLw()) {
+            if (wasVisible=win.isWinVisibleLw()) {
                 
                 int transit = WindowManagerPolicy.TRANSIT_EXIT;
                 if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
@@ -1277,6 +1346,12 @@
         mKeyWaiter.releasePendingPointerLocked(win.mSession);
         mKeyWaiter.releasePendingTrackballLocked(win.mSession);
         
+        win.mRemoved = true;
+        
+        if (mInputMethodTarget == win) {
+            moveInputMethodWindowsIfNeededLocked(false);
+        }
+        
         mPolicy.removeWindowLw(win);
         win.removeLocked();
 
@@ -1501,7 +1576,7 @@
                         if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
                             transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                         }
-                        if (win.isVisibleLw() &&
+                        if (win.isWinVisibleLw() &&
                               applyAnimationLocked(win, transit, false)) {
                             win.mExiting = true;
                             mKeyWaiter.finishedKey(session, client, true,
@@ -1511,13 +1586,13 @@
                             // an exit.
                             win.mExiting = true;
                         } else {
+                            if (mInputMethodWindow == win) {
+                                mInputMethodWindow = null;
+                            }
                             win.destroySurfaceLocked();
                         }
                     }
                 }
-                if (mInputMethodWindow == win) {
-                    mInputMethodWindow = null;
-                }
                 outSurface.clear();
             }
 
@@ -1620,7 +1695,7 @@
 
     private boolean applyAnimationLocked(WindowState win,
             int transit, boolean isEntrance) {
-        if (win.mAnimating && win.mAnimationIsEntrance == isEntrance) {
+        if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
             // If we are trying to apply an animation, but already running
             // an animation of the same type, then just leave that one alone.
             return true;
@@ -1666,6 +1741,7 @@
                     Log.v(TAG, "Loaded animation " + a + " for " + win, e);
                 }
                 win.setAnimation(a);
+                win.mAnimationIsEntrance = isEntrance;
             }
         } else {
             win.clearAnimation();
@@ -2724,7 +2800,7 @@
         boolean added = false;
         for (int j=0; j<NCW; j++) {
             WindowState cwin = (WindowState)win.mChildWindows.get(j);
-            if (cwin.mSubLayer >= 0) {
+            if (!added && cwin.mSubLayer >= 0) {
                 mWindows.add(index, win);
                 index++;
                 added = true;
@@ -4645,8 +4721,8 @@
         KeyQ() {
             super(mContext);
             PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-            mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                    | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG");
+            mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+                    "KEEP_SCREEN_ON_FLAG");
             mHoldingScreen.setReferenceCounted(false);
         }
 
@@ -4769,8 +4845,7 @@
                 if (holding) {
                     mHoldingScreen.acquire();
                 } else {
-                    long curTime = SystemClock.uptimeMillis();
-                    mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT);
+                    mPolicy.screenOnStopped();
                     mHoldingScreen.release();
                 }
             }
@@ -5095,6 +5170,19 @@
             }
         }
 
+        public boolean performHapticFeedback(IWindow window, int effectId,
+                boolean always) {
+            synchronized(mWindowMap) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    return mPolicy.performHapticFeedback(
+                            windowForClientLocked(this, window), effectId, always);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+        
         void windowAddedLocked() {
             if (mSurfaceSession == null) {
                 if (localLOGV) Log.v(
@@ -5255,9 +5343,11 @@
 
         // Currently running animation.
         boolean mAnimating;
+        boolean mLocalAnimating;
         Animation mAnimation;
         boolean mAnimationIsEntrance;
         boolean mHasTransformation;
+        boolean mHasLocalTransformation;
         final Transformation mTransformation = new Transformation();
 
         // This is set after IWindowSession.relayout() has been called at
@@ -5297,6 +5387,9 @@
         // been updated for the new orientation.
         boolean mOrientationChanging;
         
+        // Is this window now (or just being) removed?
+        boolean mRemoved;
+        
         WindowState(Session s, IWindow c, WindowToken token,
                WindowState attachedWindow, WindowManager.LayoutParams a,
                int viewVisibility) {
@@ -5516,6 +5609,7 @@
             if (localLOGV) Log.v(
                 TAG, "Setting animation in " + this + ": " + anim);
             mAnimating = false;
+            mLocalAnimating = false;
             mAnimation = anim;
             mAnimation.restrictDuration(MAX_ANIMATION_DURATION);
             mAnimation.scaleCurrentDuration(mWindowAnimationScale);
@@ -5524,6 +5618,7 @@
         public void clearAnimation() {
             if (mAnimation != null) {
                 mAnimating = true;
+                mLocalAnimating = false;
                 mAnimation = null;
             }
         }
@@ -5752,13 +5847,15 @@
                 
                 if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
                     mHasTransformation = true;
-                    if (!mAnimating) {
+                    mHasLocalTransformation = true;
+                    if (!mLocalAnimating) {
                         if (DEBUG_ANIM) Log.v(
                             TAG, "Starting animation in " + this +
                             " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
                             " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale);
                         mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
                         mAnimation.setStartTime(currentTime);
+                        mLocalAnimating = true;
                         mAnimating = true;
                     }
                     mTransformation.clear();
@@ -5767,9 +5864,6 @@
                     if (DEBUG_ANIM) Log.v(
                         TAG, "Stepped animation in " + this +
                         ": more=" + more + ", xform=" + mTransformation);
-                    if (mAppToken != null && mAppToken.hasTransformation) {
-                        mTransformation.compose(mAppToken.transformation);
-                    }
                     if (more) {
                         // we're not done!
                         return true;
@@ -5780,10 +5874,19 @@
                     mAnimation = null;
                     //WindowManagerService.this.dump();
                 }
-                if (mAppToken != null && mAppToken.hasTransformation) {
+                mHasLocalTransformation = false;
+                if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
+                        && mAppToken.hasTransformation) {
+                    // When our app token is animating, we kind-of pretend like
+                    // we are as well.  Note the mLocalAnimating mAnimationIsEntrance
+                    // part of this check means that we will only do this if
+                    // our window is not currently exiting, or it is not
+                    // locally animating itself.  The idea being that one that
+                    // is exiting and doing a local animation should be removed
+                    // once that animation is done.
                     mAnimating = true;
                     mHasTransformation = true;
-                    mTransformation.set(mAppToken.transformation);
+                    mTransformation.clear();
                     return false;
                 } else if (mHasTransformation) {
                     // Little trick to get through the path below to act like
@@ -5796,10 +5899,11 @@
                 // If the display is frozen, and there is a pending animation,
                 // clear it and make sure we run the cleanup code.
                 mAnimating = true;
+                mLocalAnimating = true;
                 mAnimation = null;
             }
             
-            if (!mAnimating) {
+            if (!mAnimating && !mLocalAnimating) {
                 return false;
             }
 
@@ -5809,6 +5913,7 @@
                 + (mAppToken != null ? mAppToken.reportedVisible : false));
             
             mAnimating = false;
+            mLocalAnimating = false;
             mAnimation = null;
             mAnimLayer = mLayer;
             if (mIsImWindow) {
@@ -5817,6 +5922,7 @@
             if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
                     + " anim layer: " + mAnimLayer);
             mHasTransformation = false;
+            mHasLocalTransformation = false;
             mPolicyVisibility = mPolicyVisibilityAfterAnim;
             mTransformation.clear();
             if (mHasDrawn
@@ -5891,10 +5997,15 @@
         }
         
         void computeShownFrameLocked() {
-            final boolean selfTransformation = mHasTransformation;
-            final boolean attachedTransformation = (mAttachedWindow != null
-                    && mAttachedWindow.mHasTransformation);
-            if (selfTransformation || attachedTransformation) {
+            final boolean selfTransformation = mHasLocalTransformation;
+            Transformation attachedTransformation =
+                    (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
+                    ? mAttachedWindow.mTransformation : null;
+            Transformation appTransformation =
+                    (mAppToken != null && mAppToken.hasTransformation)
+                    ? mAppToken.transformation : null;
+            if (selfTransformation || attachedTransformation != null
+                    || appTransformation != null) {
                 // cache often used attributes locally  
                 final Rect frame = mFrame;
                 final float tmpFloats[] = mTmpFloats;
@@ -5905,8 +6016,11 @@
                 if (selfTransformation) {
                     tmpMatrix.preConcat(mTransformation.getMatrix());
                 }
-                if (attachedTransformation) {
-                    tmpMatrix.preConcat(mAttachedWindow.mTransformation.getMatrix());
+                if (attachedTransformation != null) {
+                    tmpMatrix.preConcat(attachedTransformation.getMatrix());
+                }
+                if (appTransformation != null) {
+                    tmpMatrix.preConcat(appTransformation.getMatrix());
                 }
 
                 // "convert" it into SurfaceFlinger's format
@@ -5940,8 +6054,11 @@
                     if (selfTransformation) {
                         mShownAlpha *= mTransformation.getAlpha();
                     }
-                    if (attachedTransformation) {
-                        mShownAlpha *= mAttachedWindow.mTransformation.getAlpha();
+                    if (attachedTransformation != null) {
+                        mShownAlpha *= attachedTransformation.getAlpha();
+                    }
+                    if (appTransformation != null) {
+                        mShownAlpha *= appTransformation.getAlpha();
                     }
                 } else {
                     //Log.i(TAG, "Not applying alpha transform");
@@ -5965,7 +6082,7 @@
         /**
          * Is this window visible?  It is not visible if there is no
          * surface, or we are in the process of running an exit animation
-         * that will remove the surface.
+         * that will remove the surface, or its app token has been hidden.
          */
         public boolean isVisibleLw() {
             final AppWindowToken atoken = mAppToken;
@@ -5975,6 +6092,18 @@
         }
 
         /**
+         * Is this window visible, ignoring its app token?  It is not visible
+         * if there is no surface, or we are in the process of running an exit animation
+         * that will remove the surface.
+         */
+        public boolean isWinVisibleLw() {
+            final AppWindowToken atoken = mAppToken;
+            return mSurface != null && mPolicyVisibility && !mAttachedHidden
+                    && (atoken == null || !atoken.hiddenRequested || atoken.animating)
+                    && !mExiting && !mDestroying;
+        }
+
+        /**
          * The same as isVisible(), but follows the current hidden state of
          * the associated app token, not the pending requested hidden state.
          */
@@ -6203,6 +6332,7 @@
             pw.println(prefix + "mShownAlpha=" + mShownAlpha
                   + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha);
             pw.println(prefix + "mAnimating=" + mAnimating
+                    + " mLocalAnimating=" + mLocalAnimating
                     + " mAnimationIsEntrance=" + mAnimationIsEntrance
                     + " mAnimation=" + mAnimation);
             pw.println(prefix + "XForm: has=" + mHasTransformation
@@ -6213,7 +6343,8 @@
                   + " mHasDrawn=" + mHasDrawn);
             pw.println(prefix + "mExiting=" + mExiting
                     + " mRemoveOnExit=" + mRemoveOnExit
-                    + " mDestroying=" + mDestroying);
+                    + " mDestroying=" + mDestroying
+                    + " mRemoved=" + mRemoved);
             pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
                     + " mAppFreezing=" + mAppFreezing);
         }
@@ -7959,6 +8090,9 @@
                 i--;
                 WindowState win = mDestroySurface.get(i);
                 win.mDestroying = false;
+                if (mInputMethodWindow == win) {
+                    mInputMethodWindow = null;
+                }
                 win.destroySurfaceLocked();
             } while (i > 0);
             mDestroySurface.clear();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f5efe4b..e391da3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -159,7 +159,12 @@
     static final int LOG_AM_BROADCAST_DISCARD_APP = 30025;
     static final int LOG_AM_CREATE_SERVICE = 30030;
     static final int LOG_AM_DESTROY_SERVICE = 30031;
-
+    static final int LOG_AM_PROCESS_CRASHED_TOO_MUCH = 30032;
+    static final int LOG_AM_DROP_PROCESS = 30033;
+    static final int LOG_AM_SERVICE_CRASHED_TOO_MUCH = 30034;
+    static final int LOG_AM_SCHEDULE_SERVICE_RESTART = 30035;
+    static final int LOG_AM_PROVIDER_LOST_PROCESS = 30036;
+    
     static final int LOG_BOOT_PROGRESS_AMS_READY = 3040;
     static final int LOG_BOOT_PROGRESS_ENABLE_SCREEN = 3050;
 
@@ -1787,7 +1792,7 @@
         }
     }
 
-    private final void startPausingLocked(boolean userLeaving) {
+    private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
         if (mPausingActivity != null) {
             RuntimeException e = new RuntimeException();
             Log.e(TAG, "Trying to pause when pause is already pending for "
@@ -1840,9 +1845,15 @@
 
         if (mPausingActivity != null) {
             // Have the window manager pause its key dispatching until the new
-            // activity has started...
-            prev.pauseKeyDispatchingLocked();
-            
+            // activity has started.  If we're pausing the activity just because
+            // the screen is being turned off and the UI is sleeping, don't interrupt
+            // key dispatch; the same activity will pick it up again on wakeup.
+            if (!uiSleeping) {
+                prev.pauseKeyDispatchingLocked();
+            } else {
+                if (DEBUG_PAUSE) Log.v(TAG, "Key dispatch not paused for screen off");
+            }
+
             // Schedule a pause timeout in case the app doesn't respond.
             // We don't give it much time because this directly impacts the
             // responsiveness seen by the user.
@@ -2188,7 +2199,7 @@
         // can be resumed...
         if (mResumedActivity != null) {
             if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: need to start pausing");
-            startPausingLocked(userLeaving);
+            startPausingLocked(userLeaving, false);
             return true;
         }
 
@@ -2605,8 +2616,7 @@
                 r.app.thread.scheduleNewIntent(ar, r);
                 sent = true;
             } catch (Exception e) {
-                Log.w(TAG,
-                      "Exception thrown sending new intent to " + r, e);
+                Log.w(TAG, "Exception thrown sending new intent to " + r, e);
             }
         }
         if (!sent) {
@@ -3456,7 +3466,7 @@
             if (mPausingActivity == null) {
                 if (DEBUG_PAUSE) Log.v(TAG, "Finish needs to pause: " + r);
                 if (DEBUG_USER_LEAVING) Log.v(TAG, "finish() => pause with userLeaving=false");
-                startPausingLocked(false);
+                startPausingLocked(false, false);
             }
 
         } else if (r.state != ActivityState.PAUSING) {
@@ -3590,9 +3600,7 @@
                 r.app.thread.scheduleSendResult(r, list);
                 return;
             } catch (Exception e) {
-                Log.w(TAG,
-                      "Exception thrown sending result to " + r,
-                      e);
+                Log.w(TAG, "Exception thrown sending result to " + r, e);
             }
         }
 
@@ -3778,8 +3786,7 @@
         r.configChangeFlags = 0;
         
         if (!mLRUActivities.remove(r)) {
-            Log.w(TAG, "Activity " + r
-                  + " being finished, but not in LRU list");
+            Log.w(TAG, "Activity " + r + " being finished, but not in LRU list");
         }
         
         return removedFromHistory;
@@ -4217,7 +4224,7 @@
                 } catch (RemoteException e) {
                 }
                 if (pkgUid == -1) {
-                    Log.w(TAG, "Invalid packageName:"+packageName);
+                    Log.w(TAG, "Invalid packageName:" + packageName);
                     return false;
                 }
                 if (uid == pkgUid || checkComponentPermission(
@@ -4270,7 +4277,7 @@
                 } catch (RemoteException e) {
                 }
                 if (pkgUid == -1) {
-                    Log.w(TAG, "Invalid packageName:"+packageName);
+                    Log.w(TAG, "Invalid packageName: " + packageName);
                     return;
                 }
                 restartPackageLocked(packageName, pkgUid);
@@ -4423,6 +4430,7 @@
         if (app == null) {
             Log.w(TAG, "No pending application record for pid " + pid
                     + " (IApplicationThread " + thread + "); dropping process");
+            EventLog.writeEvent(LOG_AM_DROP_PROCESS, pid);
             if (pid > 0 && pid != MY_PID) {
                 Process.killProcess(pid);
             } else {
@@ -6579,10 +6587,10 @@
             if (caller != null) {
                 r = getRecordForAppLocked(caller);
                 if (r == null) {
-                throw new SecurityException(
-                        "Unable to find app for caller " + caller
-                      + " (pid=" + Binder.getCallingPid()
-                      + ") when getting content provider " + name);
+                    throw new SecurityException(
+                            "Unable to find app for caller " + caller
+                          + " (pid=" + Binder.getCallingPid()
+                          + ") when getting content provider " + name);
                 }
             }
 
@@ -6739,6 +6747,10 @@
                             + cpi.applicationInfo.packageName + "/"
                             + cpi.applicationInfo.uid + " for provider "
                             + name + ": launching app became null");
+                    EventLog.writeEvent(LOG_AM_PROVIDER_LOST_PROCESS,
+                            cpi.applicationInfo.packageName,
+                            cpi.applicationInfo.uid, name);
+                    return null;
                 }
                 try {
                     cpr.wait();
@@ -6844,9 +6856,6 @@
                 ContentProviderRecord dst =
                     (ContentProviderRecord)r.pubProviders.get(src.info.name);
                 if (dst != null) {
-                    dst.provider = src.provider;
-                    dst.app = r;
-
                     mProvidersByClass.put(dst.info.name, dst);
                     String names[] = dst.info.authority.split(";");
                     for (int j = 0; j < names.length; j++) {
@@ -6863,6 +6872,8 @@
                         }
                     }
                     synchronized (dst) {
+                        dst.provider = src.provider;
+                        dst.app = r;
                         dst.notifyAll();
                     }
                     updateOomAdjLocked(r);
@@ -6993,7 +7004,7 @@
             if (mPausingActivity == null) {
                 if (DEBUG_PAUSE) Log.v(TAG, "Sleep needs to pause...");
                 if (DEBUG_USER_LEAVING) Log.v(TAG, "Sleep => pause with userLeaving=false");
-                startPausingLocked(false);
+                startPausingLocked(false, true);
             }
         }
     }
@@ -7499,6 +7510,8 @@
             // This process loses!
             Log.w(TAG, "Process " + app.info.processName
                     + " has crashed too many times: killing!");
+            EventLog.writeEvent(LOG_AM_PROCESS_CRASHED_TOO_MUCH,
+                    app.info.processName, app.info.uid);
             killServicesLocked(app, false);
             for (int i=mHistory.size()-1; i>=0; i--) {
                 HistoryRecord r = (HistoryRecord)mHistory.get(i);
@@ -8429,6 +8442,8 @@
                 if (sr.crashCount >= 2) {
                     Log.w(TAG, "Service crashed " + sr.crashCount
                             + " times, stopping: " + sr);
+                    EventLog.writeEvent(LOG_AM_SERVICE_CRASHED_TOO_MUCH,
+                            sr.crashCount, sr.shortName, app.pid);
                     bringDownServiceLocked(sr, true);
                 } else if (!allowRestart) {
                     bringDownServiceLocked(sr, true);
@@ -8447,7 +8462,10 @@
 
     private final void removeDyingProviderLocked(ProcessRecord proc,
             ContentProviderRecord cpr) {
-        cpr.launchingApp = null;
+        synchronized (cpr) {
+            cpr.launchingApp = null;
+            cpr.notifyAll();
+        }
         
         mProvidersByClass.remove(cpr.info.name);
         String names[] = cpr.info.authority.split(";");
@@ -8529,6 +8547,8 @@
                             break;
                         }
                     }
+                } else {
+                    i = NL;
                 }
 
                 if (i >= NL) {
@@ -8602,7 +8622,7 @@
         mProcessesOnHold.remove(app);
 
         if (restart) {
-            // We have component that still need to be running in the
+            // We have components that still need to be running in the
             // process, so re-launch it.
             mProcessNames.put(app.processName, app.info.uid, app);
             startProcessLocked(app, "restart", app.processName);
@@ -8952,6 +8972,8 @@
         r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
         Log.w(TAG, "Scheduling restart of crashed service "
                 + r.shortName + " in " + r.restartDelay + "ms");
+        EventLog.writeEvent(LOG_AM_SCHEDULE_SERVICE_RESTART,
+                r.shortName, r.restartDelay);
 
         Message msg = Message.obtain();
         msg.what = SERVICE_ERROR_MSG;
@@ -11660,7 +11682,7 @@
 
                 // We can finish this one if we have its icicle saved and
                 // it is not persistent.
-                if ((r.haveState || !r.stateNotNeeded)
+                if ((r.haveState || !r.stateNotNeeded) && !r.visible
                         && r.stopped && !r.persistent && !r.finishing) {
                     final int origSize = mLRUActivities.size();
                     destroyActivityLocked(r, true);
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 001987f..3922f39 100755
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -22,13 +22,24 @@
 import android.os.Binder;
 import android.os.IBinder;
 import com.android.internal.os.PkgUsageStats;
+import android.os.Parcel;
 import android.os.Process;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.util.Log;
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -44,35 +55,257 @@
     private static final String TAG = "UsageStats";
     static IUsageStats sService;
     private Context mContext;
-    private String mFileName;
+    // structure used to maintain statistics since the last checkin.
     final private Map<String, PkgUsageStatsExtended> mStats;
+    // Lock to update package stats. Methods suffixed by SLOCK should invoked with
+    // this lock held
+    final Object mStatsLock;
+    // Lock to write to file. Methods suffixed by FLOCK should invoked with
+    // this lock held.
+    final Object mFileLock;
+    // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
     private String mResumedPkg;
+    private File mFile;
+    //private File mBackupFile;
+    private long mLastWriteRealTime;
+    private int _FILE_WRITE_INTERVAL = 30*60*1000; //ms
+    private static final String _PREFIX_DELIMIT=".";
+    private String mFilePrefix;
+    private Calendar mCal;
+    private static final int  _MAX_NUM_FILES = 10;
+    private long mLastTime;
     
     private class PkgUsageStatsExtended {
         int mLaunchCount;
         long mUsageTime;
-        long mChgTime;
+        long mPausedTime;
+        long mResumedTime;
+        
         PkgUsageStatsExtended() {
             mLaunchCount = 0;
             mUsageTime = 0;
-            mChgTime =  SystemClock.elapsedRealtime();
         }
         void updateResume() {
             mLaunchCount ++;
-            mChgTime = SystemClock.elapsedRealtime();
+            mResumedTime = SystemClock.elapsedRealtime();
         }
         void updatePause() {
-            long currTime = SystemClock.elapsedRealtime();
-            mUsageTime += (currTime - mChgTime);
-            mChgTime = currTime;
+            mPausedTime =  SystemClock.elapsedRealtime();
+            mUsageTime += (mPausedTime - mResumedTime);
+        }
+        void clear() {
+            mLaunchCount = 0;
+            mUsageTime = 0;
         }
     }
     
-    UsageStatsService(String filename) {
-        mFileName = filename;
+    UsageStatsService(String fileName) {
         mStats = new HashMap<String, PkgUsageStatsExtended>();
+        mStatsLock = new Object();
+        mFileLock = new Object();
+        mFilePrefix = fileName;
+        mCal = Calendar.getInstance();
+        // Update current stats which are binned by date
+        String uFileName = getCurrentDateStr(mFilePrefix);
+        mFile = new File(uFileName);
+        readStatsFromFile();
+        mLastWriteRealTime = SystemClock.elapsedRealtime();
+        mLastTime = new Date().getTime();
+    }
+
+    /*
+     * Utility method to convert date into string.
+     */
+    private String getCurrentDateStr(String prefix) {
+        mCal.setTime(new Date());
+        StringBuilder sb = new StringBuilder();
+        if (prefix != null) {
+            sb.append(prefix);
+            sb.append(".");
+        }
+        int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
+        if (mm < 10) {
+            sb.append("0");
+        }
+        sb.append(mm);
+        int dd = mCal.get(Calendar.DAY_OF_MONTH);
+        if (dd < 10) {
+            sb.append("0");
+        }
+        sb.append(dd);
+        sb.append(mCal.get(Calendar.YEAR));
+        return sb.toString();
     }
     
+    private Parcel getParcelForFile(File file) throws IOException {
+        FileInputStream stream = new FileInputStream(file);
+        byte[] raw = readFully(stream);
+        Parcel in = Parcel.obtain();
+        in.unmarshall(raw, 0, raw.length);
+        in.setDataPosition(0);
+        stream.close();
+        return in;
+    }
+    
+    private void readStatsFromFile() {
+        File newFile = mFile;
+        synchronized (mFileLock) {
+            try {
+                if (newFile.exists()) {
+                    readStatsFLOCK(newFile);
+                } else {
+                    // Check for file limit before creating a new file
+                    checkFileLimitFLOCK();
+                    newFile.createNewFile();
+                }
+            } catch (IOException e) {
+                Log.w(TAG,"Error : " + e + " reading data from file:" + newFile);
+            }
+        }
+    }
+    
+    private void readStatsFLOCK(File file) throws IOException {
+        Parcel in = getParcelForFile(file);
+        while (in.dataAvail() > 0) {
+            String pkgName = in.readString();
+            PkgUsageStatsExtended pus = new PkgUsageStatsExtended();
+            pus.mLaunchCount = in.readInt();
+            pus.mUsageTime = in.readLong();
+            synchronized (mStatsLock) {
+                mStats.put(pkgName, pus);
+            }
+        }
+    }
+
+    private ArrayList<String> getUsageStatsFileListFLOCK() {
+        File dir = getUsageFilesDir();
+        if (dir == null) {
+            Log.w(TAG, "Couldnt find writable directory for usage stats file");
+            return null;
+        }
+        // Check if there are too many files in the system and delete older files
+        String fList[] = dir.list();
+        if (fList == null) {
+            return null;
+        }
+        File pre = new File(mFilePrefix);
+        String filePrefix = pre.getName();
+        // file name followed by dot
+        int prefixLen = filePrefix.length()+1;
+        ArrayList<String> fileList = new ArrayList<String>();
+        for (String file : fList) {
+            int index = file.indexOf(filePrefix);
+            if (index == -1) {
+                continue;
+            }
+            if (file.endsWith(".bak")) {
+                continue;
+            }
+            fileList.add(file);
+        }
+        return fileList;
+    }
+    
+    private File getUsageFilesDir() {
+        if (mFilePrefix == null) {
+            return null;
+        }
+        File pre = new File(mFilePrefix);
+        return new File(pre.getParent());
+    }
+    
+    private void checkFileLimitFLOCK() {
+        File dir = getUsageFilesDir();
+        if (dir == null) {
+            Log.w(TAG, "Couldnt find writable directory for usage stats file");
+            return;
+        }
+        // Get all usage stats output files
+        ArrayList<String> fileList = getUsageStatsFileListFLOCK();
+        if (fileList == null) {
+            // Strange but we dont have to delete any thing
+            return;
+        }
+        int count = fileList.size();
+        if (count <= _MAX_NUM_FILES) {
+            return;
+        }
+        // Sort files
+        Collections.sort(fileList);
+        count -= _MAX_NUM_FILES;
+        // Delete older files
+        for (int i = 0; i < count; i++) {
+            String fileName = fileList.get(i);
+            File file = new File(dir, fileName);
+            Log.i(TAG, "Deleting file : "+fileName);
+            file.delete();
+        }
+    }
+    
+    private void writeStatsToFile() {
+        synchronized (mFileLock) {
+            long currTime = new Date().getTime();
+            boolean dayChanged =  ((currTime - mLastTime) >= (24*60*60*1000));
+            long currRealTime = SystemClock.elapsedRealtime();
+            if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) &&
+                    (!dayChanged)) {
+                // wait till the next update
+                return;
+            }
+            // Get the most recent file
+            String todayStr = getCurrentDateStr(mFilePrefix);
+            // Copy current file to back up
+            File backupFile =  new File(mFile.getPath() + ".bak");
+            mFile.renameTo(backupFile);
+            try {
+                checkFileLimitFLOCK();
+                mFile.createNewFile();
+                // Write mStats to file
+                writeStatsFLOCK();
+                mLastWriteRealTime = currRealTime;
+                mLastTime = currTime;
+                if (dayChanged) {
+                    // clear stats
+                    synchronized (mStats) {
+                        mStats.clear();
+                    }
+                    mFile = new File(todayStr);
+                }
+                // Delete the backup file
+                if (backupFile != null) {
+                    backupFile.delete();
+                }
+            } catch (IOException e) {
+                Log.w(TAG, "Failed writing stats to file:" + mFile);
+                if (backupFile != null) {
+                    backupFile.renameTo(mFile);
+                }
+            }
+        }
+    }
+
+    private void writeStatsFLOCK() throws IOException {
+        FileOutputStream stream = new FileOutputStream(mFile);
+        Parcel out = Parcel.obtain();
+        writeStatsToParcelFLOCK(out);
+        stream.write(out.marshall());
+        out.recycle();
+        stream.flush();
+        stream.close();
+    }
+
+    private void writeStatsToParcelFLOCK(Parcel out) {
+        synchronized (mStatsLock) {
+            Set<String> keys = mStats.keySet();
+            for (String key : keys) {
+                PkgUsageStatsExtended pus = mStats.get(key);
+                out.writeString(key);
+                out.writeInt(pus.mLaunchCount);
+                out.writeLong(pus.mUsageTime);
+            }
+        }
+    }
+
     public void publish(Context context) {
         mContext = context;
         ServiceManager.addService(SERVICE_NAME, asBinder());
@@ -99,12 +332,14 @@
             return;
         } 
         if (localLOGV) Log.i(TAG, "started component:"+pkgName);
-        PkgUsageStatsExtended pus = mStats.get(pkgName);
-        if (pus == null) {
-            pus = new PkgUsageStatsExtended();
-            mStats.put(pkgName, pus);
+        synchronized (mStatsLock) {
+            PkgUsageStatsExtended pus = mStats.get(pkgName);
+            if (pus == null) {
+                pus = new PkgUsageStatsExtended();
+                mStats.put(pkgName, pus);
+            }
+            pus.updateResume();
         }
-        pus.updateResume();
         mResumedPkg = pkgName;
     }
 
@@ -120,13 +355,17 @@
             return;
         }
         if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
-        PkgUsageStatsExtended pus = mStats.get(pkgName);
-        if (pus == null) {
-            // Weird some error here
-            Log.w(TAG, "No package stats for pkg:"+pkgName);
-            return;
+        synchronized (mStatsLock) {
+            PkgUsageStatsExtended pus = mStats.get(pkgName);
+            if (pus == null) {
+                // Weird some error here
+                Log.w(TAG, "No package stats for pkg:"+pkgName);
+                return;
+            }
+            pus.updatePause();
         }
-        pus.updatePause();
+        // Persist data to file
+        writeStatsToFile();
     }
     
     public void enforceCallingPermission() {
@@ -145,17 +384,19 @@
                 ((pkgName = componentName.getPackageName()) == null)) {
             return null;
         }
-        PkgUsageStatsExtended pus = mStats.get(pkgName);
-        if (pus == null) {
-            return null;
+        synchronized (mStatsLock) {
+            PkgUsageStatsExtended pus = mStats.get(pkgName);
+            if (pus == null) {
+               return null;
+            }
+            return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime);
         }
-        return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime);
     }
     
     public PkgUsageStats[] getAllPkgUsageStats() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
-        synchronized (mStats) {
+        synchronized (mStatsLock) {
             Set<String> keys = mStats.keySet();
             int size = keys.size();
             if (size <= 0) {
@@ -172,21 +413,120 @@
         }
     }
     
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        StringBuilder sb = new StringBuilder();
-        synchronized (mStats) {
-            Set<String> keys = mStats.keySet();
-            for (String key: keys) {
-                PkgUsageStatsExtended ps = mStats.get(key);
-                sb.append("pkg="); 
-                sb.append(key);
-                sb.append(", launchCount=");
-                sb.append(ps.mLaunchCount);
-                sb.append(", usageTime=");
-                sb.append(ps.mUsageTime+" ms\n");
+    static byte[] readFully(FileInputStream stream) throws java.io.IOException {
+        int pos = 0;
+        int avail = stream.available();
+        byte[] data = new byte[avail];
+        while (true) {
+            int amt = stream.read(data, pos, data.length-pos);
+            if (amt <= 0) {
+                return data;
             }
+            pos += amt;
+            avail = stream.available();
+            if (avail > data.length-pos) {
+                byte[] newData = new byte[pos+avail];
+                System.arraycopy(data, 0, newData, 0, pos);
+                data = newData;
+            }
+        }
+    }
+    
+    private void collectDumpInfoFLOCK(PrintWriter pw, String[] args) {
+        List<String> fileList = getUsageStatsFileListFLOCK();
+        if (fileList == null) {
+            return;
+        }
+        final boolean isCheckinRequest = scanArgs(args, "-c");
+        Collections.sort(fileList);
+        File usageFile = new File(mFilePrefix);
+        String dirName = usageFile.getParent();
+        File dir = new File(dirName);
+        String filePrefix = usageFile.getName();
+        // file name followed by dot
+        int prefixLen = filePrefix.length()+1;
+        String todayStr = getCurrentDateStr(null);
+        for (String file : fileList) {
+            File dFile = new File(dir, file);
+            String dateStr = file.substring(prefixLen);
+            try {
+                Parcel in = getParcelForFile(dFile);
+                collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest);
+                if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) {
+                    // Delete old file after collecting info only for checkin requests
+                    dFile.delete();
+                }
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file);
+                return;
+            } catch (IOException e) {
+                Log.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
+            }      
+        }
+    }
+    
+    private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
+            String date, boolean isCheckinRequest) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Date:");
+        sb.append(date);
+        boolean first = true;
+        while (in.dataAvail() > 0) {
+            String pkgName = in.readString();
+            int launchCount = in.readInt();
+            long usageTime = in.readLong();
+            if (isCheckinRequest) {
+                if (!first) {
+                    sb.append(",");
+                }
+                sb.append(pkgName);
+                sb.append(",");
+                sb.append(launchCount);
+                sb.append(",");
+                sb.append(usageTime);
+                sb.append("ms");
+            } else {
+                if (first) {
+                    sb.append("\n");
+                }
+                sb.append("pkg=");
+                sb.append(pkgName);
+                sb.append(", launchCount=");
+                sb.append(launchCount);
+                sb.append(", usageTime=");
+                sb.append(usageTime);
+                sb.append(" ms\n");
+            }
+            first = false;
         }
         pw.write(sb.toString());
     }
+    
+    /**
+     * Searches array of arguments for the specified string
+     * @param args array of argument strings
+     * @param value value to search for
+     * @return true if the value is contained in the array
+     */
+    private static boolean scanArgs(String[] args, String value) {
+        if (args != null) {
+            for (String arg : args) {
+                if (value.equals(arg)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    @Override
+    /*
+     * The data persisted to file is parsed and the stats are computed. 
+     */
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        synchronized (mFileLock) {
+            collectDumpInfoFLOCK(pw, args);
+        }
+    }
+
 }