auto import from //branches/cupcake/...@130745
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 1625853..d8012b2 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -382,7 +382,7 @@
     
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission("android.permission.DUMP")
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump AlarmManager from from pid="
                     + Binder.getCallingPid()
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index dba8fca..e4fdd0c1 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -26,6 +26,7 @@
 import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UEventObserver;
 import android.util.EventLog;
 import android.util.Log;
@@ -61,9 +62,13 @@
     
     static final int LOG_BATTERY_LEVEL = 2722;
     static final int LOG_BATTERY_STATUS = 2723;
+    static final int LOG_BATTERY_DISCHARGE_STATUS = 2730;
     
     static final int BATTERY_SCALE = 100;    // battery capacity is a percentage
 
+    // This should probably be exposed in the API, though it's not critical
+    private static final int BATTERY_PLUGGED_NONE = 0;
+
     private final Context mContext;
     private final IBatteryStats mBatteryStats;
     
@@ -85,7 +90,10 @@
     private int mLastBatteryTemperature;
     
     private int mPlugType;
-    private int mLastPlugType;
+    private int mLastPlugType = -1; // Extra state so we can detect first run
+    
+    private long mDischargeStartTime;
+    private int mDischargeStartLevel;
     
     public BatteryService(Context context) {
         mContext = context;
@@ -145,7 +153,7 @@
         } else if (mUsbOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
         } else {
-            mPlugType = 0;
+            mPlugType = BATTERY_PLUGGED_NONE;
         }
         if (mBatteryStatus != mLastBatteryStatus ||
                 mBatteryHealth != mLastBatteryHealth ||
@@ -155,6 +163,25 @@
                 mBatteryVoltage != mLastBatteryVoltage ||
                 mBatteryTemperature != mLastBatteryTemperature) {
             
+            if (mPlugType != mLastPlugType) {
+                if (mLastPlugType == BATTERY_PLUGGED_NONE) {
+                    // discharging -> charging
+                    
+                    // There's no value in this data unless we've discharged at least once and the
+                    // battery level has changed; so don't log until it does.
+                    if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryLevel) {
+                        long duration = SystemClock.elapsedRealtime() - mDischargeStartTime;
+                        EventLog.writeEvent(LOG_BATTERY_DISCHARGE_STATUS, duration,
+                                mDischargeStartLevel, mBatteryLevel);
+                        // make sure we see a discharge event before logging again
+                        mDischargeStartTime = 0; 
+                    }
+                } else if (mPlugType == BATTERY_PLUGGED_NONE) {
+                    // charging -> discharging or we just powered up
+                    mDischargeStartTime = SystemClock.elapsedRealtime();
+                    mDischargeStartLevel = mBatteryLevel;
+                }
+            }
             if (mBatteryStatus != mLastBatteryStatus ||
                     mBatteryHealth != mLastBatteryHealth ||
                     mBatteryPresent != mLastBatteryPresent ||
@@ -187,7 +214,7 @@
         Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         try {
-            mBatteryStats.setOnBattery(mPlugType == 0);
+            mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE);
         } catch (RemoteException e) {
             // Should never happen.
         }
@@ -233,7 +260,7 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission("android.permission.DUMP")
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             
             pw.println("Permission Denial: can't dump Battery service from from pid="
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 65e3650..760988d 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,7 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.provider.Sync;
 import android.util.EventLog;
 import android.util.Log;
 
@@ -333,6 +334,32 @@
         }
     }
 
+    /**
+     * @see ConnectivityManager#getBackgroundDataSetting()
+     */
+    public boolean getBackgroundDataSetting() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.BACKGROUND_DATA, 1) == 1;
+    }
+    
+    /**
+     * @see ConnectivityManager#setBackgroundDataSetting(boolean)
+     */
+    public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
+                "ConnectivityService");
+        
+        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
+
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
+        
+        Intent broadcast = new Intent(
+                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+        mContext.sendBroadcast(broadcast);
+    }    
+    
     private int getNumConnectedNetworks() {
         int numConnectedNets = 0;
 
@@ -632,7 +659,7 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump ConnectivityService from from pid="
                     + Binder.getCallingPid()
diff --git a/services/java/com/android/server/FallbackCheckinService.java b/services/java/com/android/server/FallbackCheckinService.java
new file mode 100644
index 0000000..cf22446
--- /dev/null
+++ b/services/java/com/android/server/FallbackCheckinService.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.ICheckinService;
+import android.os.IParentalControlCallback;
+import android.util.Log;
+
+import java.io.IOException;
+
+import com.android.internal.os.RecoverySystem;
+import com.google.android.net.ParentalControlState;
+
+/**
+ * @hide
+ */
+public final class FallbackCheckinService extends ICheckinService.Stub {
+    static final String TAG = "FallbackCheckinService";
+    final Context mContext;
+    
+    public FallbackCheckinService(Context context) {
+        mContext = context;
+    }
+
+    public boolean checkin() {
+        return false;  // failure, because not implemented
+    }
+
+    public void reportCrashSync(byte[] crashData) {
+    }
+
+    public void reportCrashAsync(byte[] crashData) {
+    }
+
+    public void masterClear() {
+        if (mContext.checkCallingOrSelfPermission("android.permission.MASTER_CLEAR") !=
+                PackageManager.PERMISSION_GRANTED) {
+            Log.e(TAG, "Permission Denial: can't invoke masterClear from "
+                    + "pid=" + Binder.getCallingPid() + ", "
+                    + "uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        // Save the android ID so the new system can get it erased.
+        try {
+            RecoverySystem.rebootAndWipe();
+        } catch (IOException e) {
+            Log.e(TAG, "Reboot for masterClear() failed", e);
+        }
+    }
+
+    public void getParentalControlState(IParentalControlCallback p, String requestingApp)
+            throws android.os.RemoteException {
+        ParentalControlState state = new ParentalControlState();
+        state.isEnabled = false;
+        p.onResult(state);
+    }
+}
diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java
index 4be9ed5..5ef0fb9 100644
--- a/services/java/com/android/server/GadgetService.java
+++ b/services/java/com/android/server/GadgetService.java
@@ -16,57 +16,127 @@
 
 package com.android.server;
 
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.PackageItemInfo;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.gadget.GadgetManager;
 import android.gadget.GadgetInfo;
+import android.net.Uri;
 import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.AttributeSet;
+import android.util.Config;
 import android.util.Log;
 import android.util.Xml;
+import android.widget.RemoteViews;
 
+import java.io.IOException;
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.HashMap;
+import java.util.HashSet;
 
 import com.android.internal.gadget.IGadgetService;
+import com.android.internal.gadget.IGadgetHost;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 class GadgetService extends IGadgetService.Stub
 {
     private static final String TAG = "GadgetService";
+    private static final boolean LOGD = Config.LOGD || false;
+
+    private static final String SETTINGS_FILENAME = "gadgets.xml";
+    private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
+
+    /*
+     * When identifying a Host or Provider based on the calling process, use the uid field.
+     * When identifying a Host or Provider based on a package manager broadcast, use the
+     * package given.
+     */
+
+    static class Provider {
+        int uid;
+        GadgetInfo info;
+        ArrayList<GadgetId> instances = new ArrayList();
+        PendingIntent broadcast;
+        
+        int tag;    // for use while saving state (the index)
+    }
+
+    static class Host {
+        int uid;
+        int hostId;
+        String packageName;
+        ArrayList<GadgetId> instances = new ArrayList();
+        IGadgetHost callbacks;
+        
+        int tag;    // for use while saving state (the index)
+    }
 
     static class GadgetId {
         int gadgetId;
-        String hostPackage;
-        GadgetInfo info;
+        Provider provider;
+        RemoteViews views;
+        Host host;
     }
 
     Context mContext;
     PackageManager mPackageManager;
-    ArrayList<GadgetInfo> mInstalledProviders;
+    AlarmManager mAlarmManager;
+    ArrayList<Provider> mInstalledProviders = new ArrayList();
     int mNextGadgetId = 1;
     ArrayList<GadgetId> mGadgetIds = new ArrayList();
+    ArrayList<Host> mHosts = new ArrayList();
 
     GadgetService(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
-        mInstalledProviders = getGadgetList();
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        
+        getGadgetList();
+        loadStateLocked();
+
+        // Register for the boot completed broadcast, so we can send the
+        // ENABLE broacasts.  If we try to send them now, they time out,
+        // because the system isn't ready to handle them yet.
+        mContext.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+
+        // Register for broadcasts about package install, etc., so we can
+        // 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);
     }
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump from from pid="
                     + Binder.getCallingPid()
@@ -78,7 +148,7 @@
             int N = mInstalledProviders.size();
             pw.println("Providers: (size=" + N + ")");
             for (int i=0; i<N; i++) {
-                GadgetInfo info = mInstalledProviders.get(i);
+                GadgetInfo info = mInstalledProviders.get(i).info;
                 pw.println("  [" + i + "] provder=" + info.provider
                         + " min=(" + info.minWidth + "x" + info.minHeight + ")"
                         + " updatePeriodMillis=" + info.updatePeriodMillis
@@ -89,58 +159,175 @@
             pw.println("GadgetIds: (size=" + N + ")");
             for (int i=0; i<N; i++) {
                 GadgetId id = mGadgetIds.get(i);
-                pw.println("  [" + i + "] gadgetId=" + id.gadgetId + " host=" + id.hostPackage
-                        + " provider=" + (id.info == null ? "null" : id.info.provider));
+                pw.println("  [" + i + "] gadgetId=" + id.gadgetId
+                        + " host=" + id.host.hostId + "/" + id.host.packageName + " provider="
+                        + (id.provider == null ? "null" : id.provider.info.provider)
+                        + " host.callbacks=" + (id.host != null ? id.host.callbacks : "(no host)")
+                        + " views=" + id.views);
+            }
+
+            N = mHosts.size();
+            pw.println("Hosts: (size=" + N + ")");
+            for (int i=0; i<N; i++) {
+                Host host = mHosts.get(i);
+                pw.println("  [" + i + "] packageName=" + host.packageName + " uid=" + host.uid
+                        + " hostId=" + host.hostId + " callbacks=" + host.callbacks
+                        + " instances.size=" + host.instances.size());
             }
         }
     }
 
-    public int allocateGadgetId(String hostPackage) {
+    public int allocateGadgetId(String packageName, int hostId) {
+        int callingUid = enforceCallingUid(packageName);
         synchronized (mGadgetIds) {
-            // TODO: Check for pick permission
             int gadgetId = mNextGadgetId++;
 
+            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+
             GadgetId id = new GadgetId();
             id.gadgetId = gadgetId;
-            id.hostPackage = hostPackage;
+            id.host = host;
 
+            host.instances.add(id);
             mGadgetIds.add(id);
 
+            saveStateLocked();
+
             return gadgetId;
         }
     }
 
     public void deleteGadgetId(int gadgetId) {
         synchronized (mGadgetIds) {
-            String callingPackage = getCallingPackage();
+            int callingUid = getCallingUid();
             final int N = mGadgetIds.size();
             for (int i=0; i<N; i++) {
                 GadgetId id = mGadgetIds.get(i);
-                if (canAccessGadgetId(id, callingPackage)) {
-                    mGadgetIds.remove(i);
-                    // TODO: Notify someone?
+                if (id.provider != null && canAccessGadgetId(id, callingUid)) {
+                    deleteGadgetLocked(id);
+
+                    saveStateLocked();
                     return;
                 }
             }
         }
     }
 
+    public void deleteHost(int hostId) {
+        synchronized (mGadgetIds) {
+            int callingUid = getCallingUid();
+            Host host = lookupHostLocked(callingUid, hostId);
+            if (host != null) {
+                deleteHostLocked(host);
+                saveStateLocked();
+            }
+        }
+    }
+
+    public void deleteAllHosts() {
+        synchronized (mGadgetIds) {
+            int callingUid = getCallingUid();
+            final int N = mHosts.size();
+            boolean changed = false;
+            for (int i=0; i<N; i++) {
+                Host host = mHosts.get(i);
+                if (host.uid == callingUid) {
+                    deleteHostLocked(host);
+                    changed = true;
+                }
+            }
+            if (changed) {
+                saveStateLocked();
+            }
+        }
+    }
+
+    void deleteHostLocked(Host host) {
+        final int N = host.instances.size();
+        for (int i=0; i<N; i++) {
+            GadgetId id = host.instances.get(i);
+            deleteGadgetLocked(id);
+        }
+        host.instances.clear();
+        mHosts.remove(host);
+        // it's gone or going away, abruptly drop the callback connection
+        host.callbacks = null;
+    }
+
+    void deleteGadgetLocked(GadgetId id) {
+        Host host = id.host;
+        host.instances.remove(id);
+        pruneHostLocked(host);
+
+        mGadgetIds.remove(id);
+
+        Provider p = id.provider;
+        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.setComponent(p.info.provider);
+            intent.putExtra(GadgetManager.EXTRA_GADGET_ID, id.gadgetId);
+            mContext.sendBroadcast(intent);
+            if (p.instances.size() == 0) {
+                // cancel the future updates
+                cancelBroadcasts(p);
+
+                // send the broacast saying that the provider is not in use any more
+                intent = new Intent(GadgetManager.GADGET_DISABLED_ACTION);
+                intent.setComponent(p.info.provider);
+                mContext.sendBroadcast(intent);
+            }
+        }
+    }
+
+    void cancelBroadcasts(Provider p) {
+        if (p.broadcast != null) {
+            mAlarmManager.cancel(p.broadcast);
+            long token = Binder.clearCallingIdentity();
+            try {
+                p.broadcast.cancel();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            p.broadcast = null;
+        }
+    }
+
     public void bindGadgetId(int gadgetId, ComponentName provider) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BIND_GADGET,
+                "bindGagetId gadgetId=" + gadgetId + " provider=" + provider);
         synchronized (mGadgetIds) {
             GadgetId id = lookupGadgetIdLocked(gadgetId);
             if (id == null) {
-                throw new IllegalArgumentException("bad gadgetId"); // TODO: use a better exception
+                throw new IllegalArgumentException("bad gadgetId");
             }
-            if (id.info != null) {
+            if (id.provider != null) {
                 throw new IllegalArgumentException("gadgetId " + gadgetId + " already bound to "
-                        + id.info.provider);
+                        + id.provider.info.provider);
             }
-            GadgetInfo info = lookupGadgetInfoLocked(provider);
-            if (info == null) {
+            Provider p = lookupProviderLocked(provider);
+            if (p == null) {
                 throw new IllegalArgumentException("not a gadget provider: " + provider);
             }
 
-            id.info = info;
+            id.provider = p;
+            p.instances.add(id);
+            int instancesSize = p.instances.size();
+            if (instancesSize == 1) {
+                // tell the provider that it's ready
+                sendEnableIntentLocked(p);
+            }
+
+            // send an update now -- We need this update now, and just for this gadgetId.
+            // It's less critical when the next one happens, so when we schdule the next one,
+            // we add updatePeriodMillis to its start time.  That time will have some slop,
+            // but that's okay.
+            sendUpdateIntentLocked(p, new int[] { gadgetId });
+
+            // schedule the future updates
+            registerForBroadcastsLocked(p, getGadgetIds(p));
+            saveStateLocked();
         }
     }
 
@@ -148,7 +335,17 @@
         synchronized (mGadgetIds) {
             GadgetId id = lookupGadgetIdLocked(gadgetId);
             if (id != null) {
-                return id.info;
+                return id.provider.info;
+            }
+            return null;
+        }
+    }
+
+    public RemoteViews getGadgetViews(int gadgetId) {
+        synchronized (mGadgetIds) {
+            GadgetId id = lookupGadgetIdLocked(gadgetId);
+            if (id != null) {
+                return id.views;
             }
             return null;
         }
@@ -156,49 +353,175 @@
 
     public List<GadgetInfo> getInstalledProviders() {
         synchronized (mGadgetIds) {
-            return new ArrayList<GadgetInfo>(mInstalledProviders);
+            final int N = mInstalledProviders.size();
+            ArrayList<GadgetInfo> result = new ArrayList(N);
+            for (int i=0; i<N; i++) {
+                result.add(mInstalledProviders.get(i).info);
+            }
+            return result;
         }
     }
 
-    boolean canAccessGadgetId(GadgetId id, String callingPackage) {
-        if (id.hostPackage.equals(callingPackage)) {
+    public void updateGadgetIds(int[] gadgetIds, RemoteViews views) {
+        if (gadgetIds == null) {
+            throw new IllegalArgumentException("bad gadgetIds");
+        }
+        if (gadgetIds.length == 0) {
+            return;
+        }
+        final int N = gadgetIds.length;
+
+        synchronized (mGadgetIds) {
+            for (int i=0; i<N; i++) {
+                GadgetId id = lookupGadgetIdLocked(gadgetIds[i]);
+                updateGadgetInstanceLocked(id, views);
+            }
+        }
+    }
+
+    public void updateGadgetProvider(ComponentName provider, RemoteViews views) {
+        synchronized (mGadgetIds) {
+            Provider p = lookupProviderLocked(provider);
+            if (p == null) {
+                Log.w(TAG, "updateGadget: provider doesn't exist: " + provider);
+                return;
+            }
+            ArrayList<GadgetId> instances = p.instances;
+            final int N = instances.size();
+            for (int i=0; i<N; i++) {
+                GadgetId id = instances.get(i);
+                updateGadgetInstanceLocked(id, views);
+            }
+        }
+    }
+
+    void updateGadgetInstanceLocked(GadgetId id, RemoteViews views) {
+        // allow for stale gadgetIds and other badness
+        // lookup also checks that the calling process can access the gadget id
+        // drop unbound gadget ids (shouldn't be possible under normal circumstances)
+        if (id != null && id.provider != null) {
+            id.views = views;
+
+            // is anyone listening?
+            if (id.host.callbacks != null) {
+                try {
+                    // 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
+                    // we know that this host is still referenced by this instance.
+                    id.host.callbacks = null;
+                }
+            }
+        }
+    }
+
+    public int[] startListening(IGadgetHost callbacks, String packageName, int hostId,
+            List<RemoteViews> updatedViews) {
+        int callingUid = enforceCallingUid(packageName);
+        synchronized (mGadgetIds) {
+            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+            host.callbacks = callbacks;
+
+            updatedViews.clear();
+
+            ArrayList<GadgetId> instances = host.instances;
+            int N = instances.size();
+            int[] updatedIds = new int[N];
+            for (int i=0; i<N; i++) {
+                GadgetId id = instances.get(i);
+                updatedIds[i] = id.gadgetId;
+                updatedViews.add(id.views);
+            }
+            return updatedIds;
+        }
+    }
+
+    public void stopListening(int hostId) {
+        synchronized (mGadgetIds) {
+            Host host = lookupHostLocked(getCallingUid(), hostId);
+            host.callbacks = null;
+            pruneHostLocked(host);
+        }
+    }
+
+    boolean canAccessGadgetId(GadgetId id, int callingUid) {
+        if (id.host.uid == callingUid) {
+            // Apps hosting the gadget have access to it.
             return true;
         }
-        if (id.info != null && id.info.provider.getPackageName().equals(callingPackage)) {
+        if (id.provider != null && id.provider.uid == callingUid) {
+            // Apps providing the gadget have access to it (if the gadgetId has been bound)
             return true;
         }
-        // TODO: Check for the pick permission
-        //if (has permission) {
-        //    return true;
-        //}
-        //return false;
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_GADGET)
+                == PackageManager.PERMISSION_GRANTED) {
+            // Apps that can bind have access to all gadgetIds.
+            return true;
+        }
+        // Nobody else can access it.
+        // TODO: convert callingPackage over to use UID-based checking instead
+        // TODO: our temp solution is to short-circuit this security check
         return true;
     }
 
-    private GadgetId lookupGadgetIdLocked(int gadgetId) {
-        String callingPackage = getCallingPackage();
+   GadgetId lookupGadgetIdLocked(int gadgetId) {
+        int callingUid = getCallingUid();
         final int N = mGadgetIds.size();
         for (int i=0; i<N; i++) {
             GadgetId id = mGadgetIds.get(i);
-            if (canAccessGadgetId(id, callingPackage)) {
+            if (id.gadgetId == gadgetId && canAccessGadgetId(id, callingUid)) {
                 return id;
             }
         }
         return null;
     }
 
-    GadgetInfo lookupGadgetInfoLocked(ComponentName provider) {
+    Provider lookupProviderLocked(ComponentName provider) {
         final int N = mInstalledProviders.size();
         for (int i=0; i<N; i++) {
-            GadgetInfo info = mInstalledProviders.get(i);
-            if (info.provider.equals(provider)) {
-                return info;
+            Provider p = mInstalledProviders.get(i);
+            if (p.info.provider.equals(provider)) {
+                return p;
             }
         }
         return null;
     }
 
-    ArrayList<GadgetInfo> getGadgetList() {
+    Host lookupHostLocked(int uid, int hostId) {
+        final int N = mHosts.size();
+        for (int i=0; i<N; i++) {
+            Host h = mHosts.get(i);
+            if (h.uid == uid && h.hostId == hostId) {
+                return h;
+            }
+        }
+        return null;
+    }
+
+    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
+        final int N = mHosts.size();
+        for (int i=0; i<N; i++) {
+            Host h = mHosts.get(i);
+            if (h.hostId == hostId && h.packageName.equals(packageName)) {
+                return h;
+            }
+        }
+        Host host = new Host();
+        host.packageName = packageName;
+        host.uid = uid;
+        host.hostId = hostId;
+        mHosts.add(host);
+        return host;
+    }
+
+    void pruneHostLocked(Host host) {
+        if (host.instances.size() == 0 && host.callbacks == null) {
+            mHosts.remove(host);
+        }
+    }
+
+    void getGadgetList() {
         PackageManager pm = mPackageManager;
 
         // TODO: We have these as different actions.  I wonder if it makes more sense to
@@ -208,29 +531,96 @@
         List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
 
-        ArrayList<GadgetInfo> result = new ArrayList<GadgetInfo>();
-
         final int N = broadcastReceivers.size();
         for (int i=0; i<N; i++) {
             ResolveInfo ri = broadcastReceivers.get(i);
-            ActivityInfo ai = ri.activityInfo;
-            GadgetInfo gi = parseGadgetInfoXml(new ComponentName(ai.packageName, ai.name),
-                    ri.activityInfo);
-            if (gi != null) {
-                result.add(gi);
-            }
+            addProviderLocked(ri);
         }
-
-        return result;
     }
 
-    private GadgetInfo parseGadgetInfoXml(ComponentName component,
-            PackageItemInfo packageItemInfo) {
-        GadgetInfo gi = null;
+    void addProviderLocked(ResolveInfo ri) {
+        Provider p = parseGadgetInfoXml(new ComponentName(ri.activityInfo.packageName,
+                    ri.activityInfo.name), ri);
+        if (p != null) {
+            mInstalledProviders.add(p);
+        }
+    }
 
+    void removeProviderLocked(int index, Provider p) {
+        int N = p.instances.size();
+        for (int i=0; i<N; i++) {
+            GadgetId id = p.instances.get(i);
+            // Call back with empty RemoteViews
+            updateGadgetInstanceLocked(id, null);
+            // Stop telling the host about updates for this from now on
+            cancelBroadcasts(p);
+            // clear out references to this gadgetID
+            id.host.instances.remove(id);
+            mGadgetIds.remove(id);
+            id.provider = null;
+            pruneHostLocked(id.host);
+            id.host = null;
+        }
+        p.instances.clear();
+        mInstalledProviders.remove(index);
+        // no need to send the DISABLE broadcast, since the receiver is gone anyway
+        cancelBroadcasts(p);
+    }
+
+    void sendEnableIntentLocked(Provider p) {
+        Intent intent = new Intent(GadgetManager.GADGET_ENABLED_ACTION);
+        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);
+    }
+
+    void registerForBroadcastsLocked(Provider p, int[] gadgetIds) {
+        if (p.info.updatePeriodMillis > 0) {
+            // if this is the first instance, set the alarm.  otherwise,
+            // rely on the fact that we've already set it and that
+            // 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.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds);
+            intent.setComponent(p.info.provider);
+            long token = Binder.clearCallingIdentity();
+            try {
+                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            if (!alreadyRegistered) {
+                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        SystemClock.elapsedRealtime() + p.info.updatePeriodMillis,
+                        p.info.updatePeriodMillis, p.broadcast);
+            }
+        }
+    }
+    
+    static int[] getGadgetIds(Provider p) {
+        int instancesSize = p.instances.size();
+        int gadgetIds[] = new int[instancesSize];
+        for (int i=0; i<instancesSize; i++) {
+            gadgetIds[i] = p.instances.get(i).gadgetId;
+        }
+        return gadgetIds;
+    }
+
+    private Provider parseGadgetInfoXml(ComponentName component, ResolveInfo ri) {
+        Provider p = null;
+
+        ActivityInfo activityInfo = ri.activityInfo;
         XmlResourceParser parser = null;
         try {
-            parser = packageItemInfo.loadXmlMetaData(mPackageManager,
+            parser = activityInfo.loadXmlMetaData(mPackageManager,
                     GadgetManager.GADGET_PROVIDER_META_DATA);
             if (parser == null) {
                 Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for "
@@ -253,20 +643,29 @@
                 return null;
             }
 
-            gi = new GadgetInfo();
+            p = new Provider();
+            GadgetInfo info = p.info = new GadgetInfo();
 
-            gi.provider = component;
+            info.provider = component;
+            p.uid = activityInfo.applicationInfo.uid;
 
             TypedArray sa = mContext.getResources().obtainAttributes(attrs,
                     com.android.internal.R.styleable.GadgetProviderInfo);
-            gi.minWidth = sa.getDimensionPixelSize(
+            info.minWidth = sa.getDimensionPixelSize(
                     com.android.internal.R.styleable.GadgetProviderInfo_minWidth, 0);
-            gi.minHeight = sa.getDimensionPixelSize(
+            info.minHeight = sa.getDimensionPixelSize(
                     com.android.internal.R.styleable.GadgetProviderInfo_minHeight, 0);
-            gi.updatePeriodMillis = sa.getInt(
+            info.updatePeriodMillis = sa.getInt(
                     com.android.internal.R.styleable.GadgetProviderInfo_updatePeriodMillis, 0);
-            gi.initialLayout = sa.getResourceId(
+            info.initialLayout = sa.getResourceId(
                     com.android.internal.R.styleable.GadgetProviderInfo_initialLayout, 0);
+            String className = sa.getString(
+                    com.android.internal.R.styleable.GadgetProviderInfo_configure);
+            if (className != null) {
+                info.configure = new ComponentName(component.getPackageName(), className);
+            }
+            info.label = activityInfo.loadLabel(mPackageManager).toString();
+            info.icon = ri.getIconResource();
             sa.recycle();
         } catch (Exception e) {
             // Ok to catch Exception here, because anything going wrong because
@@ -276,17 +675,401 @@
         } finally {
             if (parser != null) parser.close();
         }
-        return gi;
+        return p;
     }
 
-    void sendEnabled(ComponentName provider) {
-        Intent intent = new Intent(GadgetManager.GADGET_ENABLE_ACTION);
-        intent.setComponent(provider);
-        mContext.sendBroadcast(intent);
+    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+            throw new PackageManager.NameNotFoundException();
+        }
+        return pkgInfo.applicationInfo.uid;
     }
 
-    String getCallingPackage() {
-        return mPackageManager.getNameForUid(getCallingUid());
+    int enforceCallingUid(String packageName) throws IllegalArgumentException {
+        int callingUid = getCallingUid();
+        int packageUid;
+        try {
+            packageUid = getUidForPackage(packageName);
+        } catch (PackageManager.NameNotFoundException ex) {
+            throw new IllegalArgumentException("packageName and uid don't match packageName="
+                    + packageName);
+        }
+        if (callingUid != packageUid) {
+            throw new IllegalArgumentException("packageName and uid don't match packageName="
+                    + packageName);
+        }
+        return callingUid;
+    }
+
+    void sendInitialBroadcasts() {
+        synchronized (mGadgetIds) {
+            final int N = mInstalledProviders.size();
+            for (int i=0; i<N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (p.instances.size() > 0) {
+                    sendEnableIntentLocked(p);
+                    int[] gadgetIds = getGadgetIds(p);
+                    sendUpdateIntentLocked(p, gadgetIds);
+                    registerForBroadcastsLocked(p, gadgetIds);
+                }
+            }
+        }
+    }
+
+    // only call from initialization -- it assumes that the data structures are all empty
+    void loadStateLocked() {
+        File temp = savedStateTempFile();
+        File real = savedStateRealFile();
+
+        // prefer the real file.  If it doesn't exist, use the temp one, and then copy it to the
+        // real one.  if there is both a real file and a temp one, assume that the temp one isn't
+        // fully written and delete it.
+        if (real.exists()) {
+            readStateFromFileLocked(real);
+            if (temp.exists()) {
+                temp.delete();
+            }
+        } else if (temp.exists()) {
+            readStateFromFileLocked(temp);
+            temp.renameTo(real);
+        }
+    }
+    
+    void saveStateLocked() {
+        File temp = savedStateTempFile();
+        File real = savedStateRealFile();
+
+        if (!real.exists()) {
+            // If the real one doesn't exist, it's either because this is the first time
+            // or because something went wrong while copying them.  In this case, we can't
+            // trust anything that's in temp.  In order to have the loadState code not
+            // use the temporary one until it's fully written, create an empty file
+            // for real, which will we'll shortly delete.
+            try {
+                real.createNewFile();
+            } catch (IOException e) {
+            }
+        }
+
+        if (temp.exists()) {
+            temp.delete();
+        }
+
+        writeStateToFileLocked(temp);
+
+        real.delete();
+        temp.renameTo(real);
+    }
+
+    void writeStateToFileLocked(File file) {
+        FileOutputStream stream = null;
+        int N;
+
+        try {
+            stream = new FileOutputStream(file, false);
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, "utf-8");
+            out.startDocument(null, true);
+
+            
+            out.startTag(null, "gs");
+
+            int providerIndex = 0;
+            N = mInstalledProviders.size();
+            for (int i=0; i<N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (p.instances.size() > 0) {
+                    out.startTag(null, "p");
+                    out.attribute(null, "pkg", p.info.provider.getPackageName());
+                    out.attribute(null, "cl", p.info.provider.getClassName());
+                    out.endTag(null, "h");
+                    p.tag = providerIndex;
+                    providerIndex++;
+                }
+            }
+
+            N = mHosts.size();
+            for (int i=0; i<N; i++) {
+                Host host = mHosts.get(i);
+                out.startTag(null, "h");
+                out.attribute(null, "pkg", host.packageName);
+                out.attribute(null, "id", Integer.toHexString(host.hostId));
+                out.endTag(null, "h");
+                host.tag = i;
+            }
+
+            N = mGadgetIds.size();
+            for (int i=0; i<N; i++) {
+                GadgetId id = mGadgetIds.get(i);
+                out.startTag(null, "g");
+                out.attribute(null, "id", Integer.toHexString(id.gadgetId));
+                out.attribute(null, "h", Integer.toHexString(id.host.tag));
+                if (id.provider != null) {
+                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+                }
+                out.endTag(null, "g");
+            }
+
+            out.endTag(null, "gs");
+
+            out.endDocument();
+            stream.close();
+        } catch (IOException e) {
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException ex) {
+            }
+            if (file.exists()) {
+                file.delete();
+            }
+        }
+    }
+
+    void readStateFromFileLocked(File file) {
+        FileInputStream stream = null;
+
+        boolean success = false;
+
+        try {
+            stream = new FileInputStream(file);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            int type;
+            int providerIndex = 0;
+            HashMap<Integer,Provider> loadedProviders = new HashMap();
+            do {
+                type = parser.next();
+                if (type == XmlPullParser.START_TAG) {
+                    String tag = parser.getName();
+                    if ("p".equals(tag)) {
+                        // TODO: do we need to check that this package has the same signature
+                        // as before?
+                        String pkg = parser.getAttributeValue(null, "pkg");
+                        String cl = parser.getAttributeValue(null, "cl");
+                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
+                        // if it wasn't uninstalled or something
+                        if (p != null) {
+                            loadedProviders.put(providerIndex, p);
+                        }
+                        providerIndex++;
+                    }
+                    else if ("h".equals(tag)) {
+                        Host host = new Host();
+
+                        // TODO: do we need to check that this package has the same signature
+                        // as before?
+                        host.packageName = parser.getAttributeValue(null, "pkg");
+                        try {
+                            host.uid = getUidForPackage(host.packageName);
+                            host.hostId = Integer.parseInt(
+                                    parser.getAttributeValue(null, "id"), 16);
+                            mHosts.add(host);
+                        } catch (PackageManager.NameNotFoundException ex) {
+                            // Just ignore drop this entry, as if it has been uninstalled.
+                            // We need to deal with this case because of safe mode, but there's
+                            // a bug filed about it.
+                        }
+                    }
+                    else if ("g".equals(tag)) {
+                        GadgetId id = new GadgetId();
+                        id.gadgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
+                        if (id.gadgetId >= mNextGadgetId) {
+                            mNextGadgetId = id.gadgetId + 1;
+                        }
+
+                        String providerString = parser.getAttributeValue(null, "p");
+                        if (providerString != null) {
+                            // there's no provider if it hasn't been bound yet.
+                            // maybe we don't have to save this, but it brings the system
+                            // to the state it was in.
+                            int pIndex = Integer.parseInt(providerString, 16);
+                            id.provider = loadedProviders.get(pIndex);
+                            if (false) {
+                                Log.d(TAG, "bound gadgetId=" + id.gadgetId + " to provider "
+                                        + pIndex + " which is " + id.provider);
+                            }
+                            if (id.provider == null) {
+                                // This provider is gone.  We just let the host figure out
+                                // that this happened when it fails to load it.
+                                continue;
+                            }
+                        }
+
+                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
+                        id.host = mHosts.get(hIndex);
+                        if (id.host == null) {
+                            // This host is gone.
+                            continue;
+                        }
+
+                        if (id.provider != null) {
+                            id.provider.instances.add(id);
+                        }
+                        id.host.instances.add(id);
+                        mGadgetIds.add(id);
+                    }
+                }
+            } while (type != XmlPullParser.END_DOCUMENT);
+            success = true;
+        } catch (NullPointerException e) {
+            Log.w(TAG, "failed parsing " + file, e);
+        } catch (NumberFormatException e) {
+            Log.w(TAG, "failed parsing " + file, e);
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "failed parsing " + file, e);
+        } catch (IOException e) {
+            Log.w(TAG, "failed parsing " + file, e);
+        } catch (IndexOutOfBoundsException e) {
+            Log.w(TAG, "failed parsing " + file, e);
+        }
+        try {
+            if (stream != null) {
+                stream.close();
+            }
+        } catch (IOException e) {
+        }
+
+        if (success) {
+            // delete any hosts that didn't manage to get connected (should happen)
+            // if it matters, they'll be reconnected.
+            final int N = mHosts.size();
+            for (int i=0; i<N; i++) {
+                pruneHostLocked(mHosts.get(i));
+            }
+        } else {
+            // failed reading, clean up
+            mGadgetIds.clear();
+            mHosts.clear();
+            final int N = mInstalledProviders.size();
+            for (int i=0; i<N; i++) {
+                mInstalledProviders.get(i).instances.clear();
+            }
+        }
+    }
+
+    File savedStateTempFile() {
+        return new File("/data/system/" + SETTINGS_TMP_FILENAME);
+        //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
+    }
+
+    File savedStateRealFile() {
+        return new File("/data/system/" + SETTINGS_FILENAME);
+        //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
+    }
+
+    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            //Log.d(TAG, "received " + action);
+            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                sendInitialBroadcasts();
+            } else {
+                Uri uri = intent.getData();
+                if (uri == null) {
+                    return;
+                }
+                String pkgName = uri.getSchemeSpecificPart();
+                if (pkgName == null) {
+                    return;
+                }
+                
+                if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                    synchronized (mGadgetIds) {
+                        addProvidersForPackageLocked(pkgName);
+                        saveStateLocked();
+                    }
+                }
+                else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                    synchronized (mGadgetIds) {
+                        updateProvidersForPackageLocked(pkgName);
+                        saveStateLocked();
+                    }
+                }
+                else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                    synchronized (mGadgetIds) {
+                        removeProvidersForPackageLocked(pkgName);
+                        saveStateLocked();
+                    }
+                }
+            }
+        }
+    };
+
+    // 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);
+        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        final int N = broadcastReceivers.size();
+        for (int i=0; i<N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            ActivityInfo ai = ri.activityInfo;
+            
+            if (pkgName.equals(ai.packageName)) {
+                addProviderLocked(ri);
+            }
+        }
+    }
+
+    // TODO: If there's a better way of matching an intent filter against the
+    // packages for a given package, use that.
+    void updateProvidersForPackageLocked(String pkgName) {
+        HashSet<String> keep = new HashSet();
+        Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION);
+        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        // add the missing ones and collect which ones to keep
+        int N = broadcastReceivers.size();
+        for (int i=0; i<N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            ActivityInfo ai = ri.activityInfo;
+            if (pkgName.equals(ai.packageName)) {
+                Provider p = lookupProviderLocked(new ComponentName(ai.packageName, ai.name));
+                if (p == null) {
+                    addProviderLocked(ri);
+                }
+                keep.add(ai.name);
+            }
+        }
+
+        // prune the ones we don't want to keep
+        N = mInstalledProviders.size();
+        for (int i=0; i<N; i++) {
+            Provider p = mInstalledProviders.get(i);
+            if (pkgName.equals(p.info.provider.getPackageName())
+                    && !keep.contains(p.info.provider.getClassName())) {
+                removeProviderLocked(i, p);
+            }
+        }
+    }
+
+    void removeProvidersForPackageLocked(String pkgName) {
+        int N = mInstalledProviders.size();
+        for (int i=0; i<N; i++) {
+            Provider p = mInstalledProviders.get(i);
+            if (pkgName.equals(p.info.provider.getPackageName())) {
+                removeProviderLocked(i, p);
+            }
+        }
+
+        // Delete the hosts for this package too
+        //
+        // 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++) {
+            Host host = mHosts.get(i);
+            if (pkgName.equals(host.packageName)) {
+                deleteHostLocked(host);
+            }
+        }
     }
 }
 
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 22ad7bd..40456ff 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -35,6 +35,10 @@
     private static final String TAG = "HardwareService";
 
     HardwareService(Context context) {
+        // Reset the hardware to a default state, in case this is a runtime
+        // restart instead of a fresh boot.
+        vibratorOff();
+
         mContext = context;
         PowerManager pm = (PowerManager)context.getSystemService(
                 Context.POWER_SERVICE);
@@ -201,7 +205,7 @@
                     mThread.notify();
                 }
                 mThread = null;
-                off();
+                vibratorOff();
             }
         }
     }
@@ -217,49 +221,65 @@
             mWakeLock.acquire();
         }
 
+        private void delay(long duration) {
+            if (duration > 0) {
+                long bedtime = SystemClock.uptimeMillis();
+                do {
+                    try {
+                        this.wait(duration);
+                    }
+                    catch (InterruptedException e) {
+                    }
+                    if (mDone) {
+                        break;
+                    }
+                    duration = duration
+                            - SystemClock.uptimeMillis() - bedtime;
+                } while (duration > 0);
+            }
+        }
+
         public void run() {
             synchronized (this) {
                 int index = 0;
-                boolean nextState = false;
                 long[] pattern = mPattern;
-                if (pattern[0] == 0) {
-                    index++;
-                    nextState = true;
-                }
                 int len = pattern.length;
-                long start = SystemClock.uptimeMillis();
+                long duration = 0;
 
                 while (!mDone) {
-                    if (nextState) {
-                        HardwareService.this.on();
-                    } else {
-                        HardwareService.this.off();
+                    // add off-time duration to any accumulated on-time duration 
+                    if (index < len) {
+                        duration += pattern[index++];
                     }
-                    nextState = !nextState;
-                    long bedtime = SystemClock.uptimeMillis();
-                    long duration = pattern[index];
-                    do {
-                        try {
-                            this.wait(duration);
+
+                    // sleep until it is time to start the vibrator
+                    delay(duration);
+                    if (mDone) {
+                        break;
+                    }
+
+                    if (index < len) {
+                        // read on-time duration and start the vibrator
+                        // duration is saved for delay() at top of loop
+                        duration = pattern[index++];
+                        if (duration > 0) {
+                            HardwareService.this.vibratorOn(duration);
                         }
-                        catch (InterruptedException e) {
-                        }
-                        if (mDone) {
-                            break;
-                        }
-                        duration = duration
-                                - SystemClock.uptimeMillis() - bedtime;
-                    } while (duration > 0);
-                    index++;
-                    if (index >= len) {
+                    } else {
                         if (mRepeat < 0) {
-                            HardwareService.this.off();
                             break;
                         } else {
                             index = mRepeat;
+                            duration = 0;
                         }
                     }
                 }
+                if (mDone) {
+                    // make sure vibrator is off if we were cancelled.
+                    // otherwise, it will turn off automatically 
+                    // when the last timeout expires.
+                    HardwareService.this.vibratorOff();
+                }
                 mWakeLock.release();
             }
             synchronized (HardwareService.this) {
@@ -301,6 +321,6 @@
     volatile Death mDeath;
     volatile IBinder mToken;
 
-    native static void on();
-    native static void off();
+    native static void vibratorOn(long milliseconds);
+    native static void vibratorOff();
 }
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 2bea731..855734d 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
 import android.os.UEventObserver;
 import android.util.Log;
 import android.media.AudioManager;
@@ -40,6 +42,8 @@
 
     private int mHeadsetState;
     private String mHeadsetName;
+    private boolean mAudioRouteNeedsUpdate;
+    private AudioManager mAudioManager;
 
     public HeadsetObserver(Context context) {
         mContext = context;
@@ -60,7 +64,7 @@
         }
     }
 
-    private final void init() {
+    private synchronized final void init() {
         char[] buffer = new char[1024];
 
         String newName = mHeadsetName;
@@ -80,21 +84,33 @@
             Log.e(TAG, "" , e);
         }
 
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         update(newName, newState);
     }
 
     private synchronized final void update(String newName, int newState) {
         if (newName != mHeadsetName || newState != mHeadsetState) {
+            boolean isUnplug = (newState == 0 && mHeadsetState == 1);
             mHeadsetName = newName;
             mHeadsetState = newState;
-            AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+            mAudioRouteNeedsUpdate = true;
 
-            audioManager.setWiredHeadsetOn(mHeadsetState == 1);
-            sendIntent();
+            sendIntent(isUnplug);
+
+            if (isUnplug) {
+                // It often takes >200ms to flush the audio pipeline after apps
+                // pause audio playback, but audio route changes are immediate,
+                // so delay the route change by 400ms.
+                // This could be improved once the audio sub-system provides an
+                // interface to clear the audio pipeline.
+                mHandler.sendEmptyMessageDelayed(0, 400);
+            } else {
+                updateAudioRoute();
+            }
         }
     }
 
-    private synchronized final void sendIntent() {
+    private synchronized final void sendIntent(boolean isUnplug) {
         //  Pack up the values and broadcast them to everyone
         Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -104,5 +120,25 @@
 
         // TODO: Should we require a permission?
         ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+        if (isUnplug) {
+            intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+            mContext.sendBroadcast(intent);
+        }
     }
+
+    private synchronized final void updateAudioRoute() {
+        if (mAudioRouteNeedsUpdate) {
+            mAudioManager.setWiredHeadsetOn(mHeadsetState == 1);
+            mAudioRouteNeedsUpdate = false;
+        }
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            updateAudioRoute();
+        }
+    };        
+
 }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 139aaa3..ee49365 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -30,6 +30,7 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -61,7 +62,6 @@
 import android.util.Printer;
 import android.view.IWindowManager;
 import android.view.WindowManager;
-import android.view.inputmethod.DefaultInputMethod;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
@@ -189,6 +189,11 @@
     ClientState mCurClient;
     
     /**
+     * The input context last provided by the current client.
+     */
+    IInputContext mCurInputContext;
+    
+    /**
      * The attributes last provided by the current client.
      */
     EditorInfo mCurAttribute;
@@ -216,6 +221,11 @@
     boolean mShowExplicitlyRequested;
     
     /**
+     * Set if we were forced to be shown.
+     */
+    boolean mShowForced;
+    
+    /**
      * Set if we last told the input method to show itself.
      */
     boolean mInputShown;
@@ -281,7 +291,7 @@
             } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                 mScreenOn = false;
             } else {
-                Log.e(TAG, "Unexpected intent " + intent);
+                Log.w(TAG, "Unexpected intent " + intent);
             }
 
             // Inform the current client of the change in active status
@@ -290,7 +300,8 @@
                     mCurClient.client.setActive(mScreenOn);
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Got RemoteException sending 'screen on/off' notification", e);
+                Log.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid "
+                        + mCurClient.pid + " uid " + mCurClient.uid);
             }
         }
     }
@@ -553,12 +564,24 @@
             try {
                 mCurClient.client.setActive(false);
             } catch (RemoteException e) {
-                Log.e(TAG, "Got RemoteException sending setActive(false) notification", e);
+                Log.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
+                        + mCurClient.pid + " uid " + mCurClient.uid);
             }
             mCurClient = null;
         }
     }
     
+    private int getShowFlags() {
+        int flags = 0;
+        if (mShowForced) {
+            flags |= InputMethod.SHOW_FORCED
+                    | InputMethod.SHOW_EXPLICIT;
+        } else if (mShowExplicitlyRequested) {
+            flags |= InputMethod.SHOW_EXPLICIT;
+        }
+        return flags;
+    }
+    
     InputBindResult attachNewInputLocked(boolean initial, boolean needResult) {
         if (!mBoundToMethod) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
@@ -567,16 +590,15 @@
         }
         final SessionState session = mCurClient.curSession;
         if (initial) {
-            executeOrSendMessage(session.method, mCaller.obtainMessageOO(
-                    MSG_START_INPUT, session, mCurAttribute));
+            executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
+                    MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
         } else {
-            executeOrSendMessage(session.method, mCaller.obtainMessageOO(
-                    MSG_RESTART_INPUT, session, mCurAttribute));
+            executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
+                    MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
         }
         if (mShowRequested) {
             if (DEBUG) Log.v(TAG, "Attach new input asks to show input");
-            showCurrentInputLocked(mShowExplicitlyRequested
-                    ? 0 : InputMethodManager.SHOW_IMPLICIT);
+            showCurrentInputLocked(getShowFlags());
         }
         return needResult
                 ? new InputBindResult(session.session, mCurId, mCurSeq)
@@ -584,7 +606,8 @@
     }
     
     InputBindResult startInputLocked(IInputMethodClient client,
-            EditorInfo attribute, boolean initial, boolean needResult) {
+            IInputContext inputContext, EditorInfo attribute,
+            boolean initial, boolean needResult) {
         // If no method is currently selected, do nothing.
         if (mCurMethodId == null) {
             return mNoBinding;
@@ -622,7 +645,8 @@
                 try {
                     cs.client.setActive(mScreenOn);
                 } catch (RemoteException e) {
-                    Log.e(TAG, "Got RemoteException sending setActive notification", e);
+                    Log.w(TAG, "Got RemoteException sending setActive notification to pid "
+                            + cs.pid + " uid " + cs.uid);
                 }
             }
         }
@@ -631,6 +655,7 @@
         mCurSeq++;
         if (mCurSeq <= 0) mCurSeq = 1;
         mCurClient = cs;
+        mCurInputContext = inputContext;
         mCurAttribute = attribute;
         
         // Check if the input method is changing.
@@ -659,6 +684,7 @@
         
         if (mCurToken != null) {
             try {
+                if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
                 mIWindowManager.removeWindowToken(mCurToken);
             } catch (RemoteException e) {
             }
@@ -679,6 +705,7 @@
             mCurId = info.getId();
             mCurToken = new Binder();
             try {
+                if (DEBUG) Log.v(TAG, "Adding window token: " + mCurToken);
                 mIWindowManager.addWindowToken(mCurToken,
                         WindowManager.LayoutParams.TYPE_INPUT_METHOD);
             } catch (RemoteException e) {
@@ -686,18 +713,20 @@
             return new InputBindResult(null, mCurId, mCurSeq);
         } else {
             mCurIntent = null;
-            Log.e(TAG, "Failure connecting to input method service: "
+            Log.w(TAG, "Failure connecting to input method service: "
                     + mCurIntent);
         }
         return null;
     }
     
     public InputBindResult startInput(IInputMethodClient client,
-            EditorInfo attribute, boolean initial, boolean needResult) {
+            IInputContext inputContext, EditorInfo attribute,
+            boolean initial, boolean needResult) {
         synchronized (mMethodMap) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return startInputLocked(client, attribute, initial, needResult);
+                return startInputLocked(client, inputContext, attribute,
+                        initial, needResult);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -712,6 +741,7 @@
             if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
                 mCurMethod = IInputMethod.Stub.asInterface(service);
                 if (mCurClient != null) {
+                    if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
                     executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                             MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
                     if (mCurClient != null) {
@@ -817,10 +847,12 @@
             mCurMethodId = id;
             Settings.Secure.putString(mContext.getContentResolver(),
                 Settings.Secure.DEFAULT_INPUT_METHOD, id);
-                    
-            Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
-            intent.putExtra("input_method_id", id);
-            mContext.sendBroadcast(intent);
+
+            if (ActivityManagerNative.isSystemReady()) {
+                Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
+                intent.putExtra("input_method_id", id);
+                mContext.sendBroadcast(intent);
+            }
             unbindCurrentInputLocked();
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -853,10 +885,13 @@
         if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
             mShowExplicitlyRequested = true;
         }
+        if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
+            mShowExplicitlyRequested = true;
+            mShowForced = true;
+        }
         if (mCurMethod != null) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO(
-                    MSG_SHOW_SOFT_INPUT, mShowExplicitlyRequested ? 1 : 0,
-                            mCurMethod));
+                    MSG_SHOW_SOFT_INPUT, getShowFlags(), mCurMethod));
             mInputShown = true;
         }
     }
@@ -884,11 +919,16 @@
     
     void hideCurrentInputLocked(int flags) {
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
-                && mShowExplicitlyRequested) {
+                && (mShowExplicitlyRequested || mShowForced)) {
             if (DEBUG) Log.v(TAG,
                     "Not hiding: explicit show not cancelled by non-explicit hide");
             return;
         }
+        if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
+            if (DEBUG) Log.v(TAG,
+                    "Not hiding: forced show not cancelled by not-always hide");
+            return;
+        }
         if (mInputShown && mCurMethod != null) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
                     MSG_HIDE_SOFT_INPUT, mCurMethod));
@@ -896,6 +936,7 @@
         mInputShown = false;
         mShowRequested = false;
         mShowExplicitlyRequested = false;
+        mShowForced = false;
     }
     
     public void windowGainedFocus(IInputMethodClient client,
@@ -933,7 +974,7 @@
                             // 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(0);
+                            hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS);
                         }
                     } else if (isTextEditor && (softInputMode &
                             WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
@@ -1052,7 +1093,7 @@
                 return true;
             case MSG_SHOW_SOFT_INPUT:
                 try {
-                    ((IInputMethod)msg.obj).showSoftInput(msg.arg1 != 0);
+                    ((IInputMethod)msg.obj).showSoftInput(msg.arg1);
                 } catch (RemoteException e) {
                 }
                 return true;
@@ -1065,6 +1106,7 @@
             case MSG_ATTACH_TOKEN:
                 args = (HandlerCaller.SomeArgs)msg.obj;
                 try {
+                    if (DEBUG) Log.v(TAG, "Sending attach of token: " + args.arg2);
                     ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
                 } catch (RemoteException e) {
                 }
@@ -1085,7 +1127,8 @@
                 try {
                     SessionState session = (SessionState)args.arg1;
                     setEnabledSessionInMainThread(session);
-                    session.method.startInput((EditorInfo)args.arg2);
+                    session.method.startInput((IInputContext)args.arg2,
+                            (EditorInfo)args.arg3);
                 } catch (RemoteException e) {
                 }
                 return true;
@@ -1094,7 +1137,8 @@
                 try {
                     SessionState session = (SessionState)args.arg1;
                     setEnabledSessionInMainThread(session);
-                    session.method.restartInput((EditorInfo)args.arg2);
+                    session.method.restartInput((IInputContext)args.arg2,
+                            (EditorInfo)args.arg3);
                 } catch (RemoteException e) {
                 }
                 return true;
@@ -1128,10 +1172,6 @@
         
         PackageManager pm = mContext.getPackageManager();
 
-        Object[][] buildin = {{
-                DefaultInputMethod.class.getName(),
-                DefaultInputMethod.getMetaInfo()}};
-
         List<ResolveInfo> services = pm.queryIntentServices(
                 new Intent(InputMethod.SERVICE_INTERFACE),
                 PackageManager.GET_META_DATA);
@@ -1150,39 +1190,6 @@
 
             if (DEBUG) Log.d(TAG, "Checking " + compName);
 
-            /* Built-in input methods are not currently supported... this will
-             * need to be reworked to bring them back (all input methods must
-             * now be published in a manifest).
-             */
-            /*
-            if (compName.getPackageName().equals(
-                    InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) {
-                // System build-in input methods;
-                String inputMethodName = null;
-                int kbType = 0;
-                String skbName = null;
-
-                for (int j = 0; j < buildin.length; ++j) {
-                    Object[] obj = buildin[j];
-                    if (compName.getClassName().equals(obj[0])) {
-                        InputMethodMetaInfo imp = (InputMethodMetaInfo) obj[1];
-                        inputMethodName = imp.inputMethodName;
-                    }
-                }
-
-                InputMethodMetaInfo p = new InputMethodMetaInfo(compName,
-                        inputMethodName, "");
-
-                list.add(p);
-
-                if (DEBUG) {
-                    Log.d(TAG, "Found a build-in input method " + p);
-                }
-
-                continue;
-            }
-            */
-
             try {
                 InputMethodInfo p = new InputMethodInfo(mContext, ri);
                 list.add(p);
@@ -1386,7 +1393,7 @@
     
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission("android.permission.DUMP")
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             
             pw.println("Permission Denial: can't dump InputMethodManager from from pid="
@@ -1395,8 +1402,12 @@
             return;
         }
 
+        IInputMethod method;
+        ClientState client;
+        
+        final Printer p = new PrintWriterPrinter(pw);
+        
         synchronized (mMethodMap) {
-            final Printer p = new PrintWriterPrinter(pw);
             p.println("Current Input Method Manager state:");
             int N = mMethodList.size();
             p.println("  Input Methods:");
@@ -1416,17 +1427,40 @@
             p.println("  mInputMethodIcon=" + mInputMethodIcon);
             p.println("  mInputMethodData=" + mInputMethodData);
             p.println("  mCurrentMethod=" + mCurMethodId);
-            p.println("  mCurSeq=" + mCurSeq + " mCurClient=" + mCurClient);
+            client = mCurClient;
+            p.println("  mCurSeq=" + mCurSeq + " mCurClient=" + client);
             p.println("  mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
                     + " mBoundToMethod=" + mBoundToMethod);
             p.println("  mCurToken=" + mCurToken);
             p.println("  mCurIntent=" + mCurIntent);
+            method = mCurMethod;
             p.println("  mCurMethod=" + mCurMethod);
             p.println("  mEnabledSession=" + mEnabledSession);
             p.println("  mShowRequested=" + mShowRequested
                     + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+                    + " mShowForced=" + mShowForced
                     + " mInputShown=" + mInputShown);
             p.println("  mScreenOn=" + mScreenOn);
         }
+        
+        if (client != null) {
+            p.println(" ");
+            pw.flush();
+            try {
+                client.client.asBinder().dump(fd, args);
+            } catch (RemoteException e) {
+                p.println("Input method client dead: " + e);
+            }
+        }
+        
+        if (method != null) {
+            p.println(" ");
+            pw.flush();
+            try {
+                method.asBinder().dump(fd, args);
+            } catch (RemoteException e) {
+                p.println("Input method service dead: " + e);
+            }
+        }
     }
 }
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 9874042..63b486c 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -165,6 +165,7 @@
     public static native int getScancodeState(int deviceId, int sw);
     public static native int getKeycodeState(int sw);
     public static native int getKeycodeState(int deviceId, int sw);
+    public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
     
     public static KeyEvent newKeyEvent(InputDevice device, long downTime,
             long eventTime, boolean down, int keycode, int repeatCount,
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index ed9ee79..bc6fd71 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -64,12 +64,14 @@
 import android.util.Config;
 import android.util.Log;
 
+import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.CellState;
 import com.android.internal.location.GpsLocationProvider;
-import com.android.internal.location.LocationCollector;
-import com.android.internal.location.LocationMasfClient;
-import com.android.internal.location.NetworkLocationProvider;
+import com.android.internal.location.ILocationCollector;
+import com.android.internal.location.INetworkLocationManager;
+import com.android.internal.location.INetworkLocationProvider;
 import com.android.internal.location.TrackProvider;
+import com.android.server.am.BatteryStatsService;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -77,7 +79,8 @@
  *
  * {@hide}
  */
-public class LocationManagerService extends ILocationManager.Stub {
+public class LocationManagerService extends ILocationManager.Stub
+        implements INetworkLocationManager {
     private static final String TAG = "LocationManagerService";
 
     // Minimum time interval between last known location writes, in milliseconds.
@@ -121,13 +124,15 @@
 
     private final Context mContext;
     private GpsLocationProvider mGpsLocationProvider;
-    private NetworkLocationProvider mNetworkLocationProvider;
+    private LocationProviderImpl mNetworkLocationProvider;
+    private INetworkLocationProvider mNetworkLocationInterface;
     private LocationWorkerHandler mLocationHandler;
 
     // Handler messages
     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;
 
     // Alarm manager and wakelock variables
     private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
@@ -143,6 +148,11 @@
     private boolean mWakeLockNetworkReceived = true;
     private boolean mWifiWakeLockAcquired = false;
     private boolean mCellWakeLockAcquired = false;
+    
+    private final IBatteryStats mBatteryStats;
+    
+    // The calling UID when we are in a clearCallingIdentity/restoreCallingIdentity block, or -1
+    private int mCallingUid = -1;
 
     /**
      * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
@@ -199,10 +209,7 @@
     private int mSignalStrength = -1;
 
     // Location collector
-    private LocationCollector mCollector;
-
-    // Location MASF service
-    private LocationMasfClient mMasfClient;
+    private ILocationCollector mCollector;
 
     // Wifi Manager
     private WifiManager mWifiManager;
@@ -417,15 +424,9 @@
 
     private void _loadProvidersNoSync() {
         // Attempt to load "real" providers first
-        if (NetworkLocationProvider.isSupported()) {
-            // Create a network location provider
-            mNetworkLocationProvider = new NetworkLocationProvider(mContext, mMasfClient);
-            LocationProviderImpl.addProvider(mNetworkLocationProvider);
-        }
-
         if (GpsLocationProvider.isSupported()) {
             // Create a gps location provider
-            mGpsLocationProvider = new GpsLocationProvider(mContext, mCollector);
+            mGpsLocationProvider = new GpsLocationProvider(mContext);
             LocationProviderImpl.addProvider(mGpsLocationProvider);
         }
 
@@ -509,18 +510,15 @@
             Log.d(TAG, "Constructed LocationManager Service");
         }
 
-        // Initialize the LocationMasfClient
-        mMasfClient = new LocationMasfClient(mContext);
-
-        // Create location collector
-        mCollector = new LocationCollector(mMasfClient);
-
         // Alarm manager, needs to be done before calling loadProviders() below
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
 
         // Create a wake lock, needs to be done before calling loadProviders() below
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+        
+        // Battery statistics service to be notified when GPS turns on or off
+        mBatteryStats = BatteryStatsService.getService();
 
         // Load providers
         loadProviders();
@@ -560,13 +558,27 @@
         if (mWifiManager != null) {
             List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
             if (wifiScanResults != null && wifiScanResults.size() != 0) {
-                if (mNetworkLocationProvider != null) {
-                    mNetworkLocationProvider.updateWifiScanResults(wifiScanResults);
+                if (mNetworkLocationInterface != null) {
+                    mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
                 }
             }
         }
     }
 
+    public void setNetworkLocationProvider(INetworkLocationProvider provider) {
+        mLocationHandler.removeMessages(MESSAGE_SET_NETWORK_LOCATION_PROVIDER);
+        Message m = Message.obtain(mLocationHandler, 
+                MESSAGE_SET_NETWORK_LOCATION_PROVIDER, provider);
+        mLocationHandler.sendMessageAtFrontOfQueue(m);
+    }
+
+    public void setLocationCollector(ILocationCollector collector) {
+        mCollector = collector;
+        if (mGpsLocationProvider != null) {
+                mGpsLocationProvider.setLocationCollector(mCollector);
+        }
+    }
+
     private WifiManager.WifiLock getWifiWakelock() {
         if (mWifiLock == null && mWifiManager != null) {
             mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
@@ -690,7 +702,8 @@
             boolean shouldBeEnabled = isAllowedBySettings(name);
 
             // Collection is only allowed when network provider is being used
-            if (p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
+            if (mCollector != null &&
+                    p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
                 mCollector.updateNetworkProviderStatus(shouldBeEnabled);
             }
 
@@ -847,7 +860,7 @@
         }
     }
 
-    private void _requestLocationUpdates(String provider,
+    private synchronized void _requestLocationUpdates(String provider,
             long minTime, float minDistance, Receiver receiver) {
         Object key = receiver.getKey();
         if (Config.LOGD) {
@@ -864,6 +877,7 @@
         String[] packages = getPackageNames();
 
         // so wakelock calls will succeed
+        mCallingUid = getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
             UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages);
@@ -889,12 +903,16 @@
                     oldRecord.dispose();
                 }
 
-                if (impl instanceof NetworkLocationProvider) {
-                    ((NetworkLocationProvider) impl).addListener(packages);
-                }
-
                 boolean isProviderEnabled = isAllowedBySettings(provider);
                 if (isProviderEnabled) {
+                    if (provider.equals(LocationManager.GPS_PROVIDER)) {
+                        try {
+                            mBatteryStats.noteRequestGpsOn(mCallingUid);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
+                        }
+                    }
+                    
                     long minTimeForProvider = getMinTime(provider);
                     impl.setMinTime(minTimeForProvider);
                     impl.enableLocationTracking(true);
@@ -904,7 +922,6 @@
                     mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
                     Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
                     mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
-
                 } else {
                     try {
                         // Notify the listener that updates are currently disabled
@@ -919,6 +936,7 @@
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
+            mCallingUid = -1;
         }
     }
 
@@ -942,13 +960,14 @@
         }
     }
 
-    private void _removeUpdates(Receiver receiver) {
+    private synchronized void _removeUpdates(Receiver receiver) {
         Object key = receiver.getKey();
         if (Config.LOGD) {
             Log.d(TAG, "_removeUpdates: listener = " + key);
         }
 
         // so wakelock calls will succeed
+        mCallingUid = getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLocationListeners) {
@@ -964,8 +983,8 @@
                     // Call dispose() on the obsolete update records.
                     for (UpdateRecord record : oldRecords.values()) {
                         if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
-                            if (mNetworkLocationProvider != null) {
-                                mNetworkLocationProvider.removeListener(record.mPackages);
+                            if (mNetworkLocationInterface != null) {
+                                mNetworkLocationInterface.removeListener(record.mPackages);
                             }
                         }
                         record.dispose();
@@ -973,6 +992,14 @@
                     // Accumulate providers
                     providers.addAll(oldRecords.keySet());
                 }
+                
+                if (providers.contains("gps")) {
+                    try {
+                        mBatteryStats.noteRequestGpsOff(mCallingUid);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
+                    }
+                }
 
                 mLocationListeners.remove(key);
                 mLastFixBroadcast.remove(key);
@@ -1010,6 +1037,7 @@
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
+            mCallingUid = -1;
         }
     }
 
@@ -1426,7 +1454,7 @@
             }
             writeLastKnownLocation(provider, loc);
 
-            if (p instanceof NetworkLocationProvider) {
+            if (p instanceof INetworkLocationProvider) {
                 mWakeLockNetworkReceived = true;
             } else if (p instanceof GpsLocationProvider) {
                 // Gps location received signal is in NetworkStateBroadcastReceiver
@@ -1570,6 +1598,17 @@
                     // Update wakelock status so the next alarm is set before releasing wakelock
                     updateWakelockStatus(mScreenOn);
                     releaseWakeLock();
+                } else if (msg.what == MESSAGE_SET_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();
+                    }
                 }
             } catch (Exception e) {
                 // Log, don't crash!
@@ -1590,7 +1629,9 @@
                 mLastCellState = new CellState(mTelephonyManager, cellLocation, asu);
 
                 // Notify collector
-                mCollector.updateCellState(mLastCellState);
+                if (mCollector != null) {
+                    mCollector.updateCellState(mLastCellState);
+                }
 
                 // Updates providers
                 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
@@ -1648,7 +1689,9 @@
                 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
 
                 // Notify collector battery state
-                mCollector.updateBatteryState(scale, level, plugged);
+                if (mCollector != null) {
+                    mCollector.updateBatteryState(scale, level, plugged);
+                }
             }
         }
     }
@@ -1666,9 +1709,11 @@
                 }
 
                 // Notify provider and collector of Wifi scan results
-                mCollector.updateWifiScanResults(wifiScanResults);
-                if (mNetworkLocationProvider != null) {
-                    mNetworkLocationProvider.updateWifiScanResults(wifiScanResults);
+                if (mCollector != null) {
+                    mCollector.updateWifiScanResults(wifiScanResults);
+                }
+                if (mNetworkLocationInterface != null) {
+                    mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
                 }
 
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
@@ -1702,8 +1747,8 @@
                 }
 
                 // Notify network provider of current wifi enabled state
-                if (mNetworkLocationProvider != null) {
-                    mNetworkLocationProvider.updateWifiEnabledState(enabled);
+                if (mNetworkLocationInterface != null) {
+                    mNetworkLocationInterface.updateWifiEnabledState(enabled);
                 }
 
             } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
@@ -1821,8 +1866,8 @@
         }
 
         // Notify NetworkLocationProvider
-        if (mNetworkLocationProvider != null) {
-            mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
+        if (mNetworkLocationInterface != null) {
+            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
         }
 
         // Acquire wifi lock
@@ -1845,6 +1890,14 @@
                     && mGpsLocationProvider.isLocationTracking();
         if (gpsActive) {
             mGpsLocationProvider.startNavigating();
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mBatteryStats.noteStartGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
+            } catch (RemoteException e) {
+                Log.w(TAG, "RemoteException calling noteStartGps on BatteryStatsService", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1853,6 +1906,14 @@
                     && mGpsLocationProvider.isLocationTracking();
         if (gpsActive) {
             mGpsLocationProvider.stopNavigating();
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mBatteryStats.noteStopGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
+            } catch (RemoteException e) {
+                Log.w(TAG, "RemoteException calling noteStopGps on BatteryStatsService", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1877,7 +1938,6 @@
         }
 
         if (!mScreenOn) {
-
             // Stop the gps
             stopGps();
         }
@@ -1889,8 +1949,8 @@
         }
 
         // Notify NetworkLocationProvider
-        if (mNetworkLocationProvider != null) {
-            mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
+        if (mNetworkLocationInterface != null) {
+            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
         }
 
         // Release wake lock
@@ -1907,14 +1967,10 @@
 
     public String getFromLocation(double latitude, double longitude, int maxResults,
         String language, String country, String variant, String appName, List<Address> addrs) {
-        try {
-            Locale locale = new Locale(language, country, variant);
-            mMasfClient.reverseGeocode(locale, appName, latitude, longitude, maxResults, addrs);
-            return null;
-        } catch(IOException e) {
-            return e.getMessage();
-        } catch(Exception e) {
-            Log.e(TAG, "getFromLocation got exception:", e);
+        if (mNetworkLocationInterface != null) {
+            return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
+                    language, country, variant, appName, addrs);
+        } else {
             return null;
         }
     }
@@ -1923,17 +1979,11 @@
         double lowerLeftLatitude, double lowerLeftLongitude,
         double upperRightLatitude, double upperRightLongitude, int maxResults,
         String language, String country, String variant, String appName, List<Address> addrs) {
-
-        try {
-            Locale locale = new Locale(language, country, variant);
-            mMasfClient.forwardGeocode(locale, appName, locationName,
-                lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
-                maxResults, addrs);
-            return null;
-        } catch(IOException e) {
-            return e.getMessage();
-        } catch(Exception e) {
-            Log.e(TAG, "getFromLocationName got exception:", e);
+        if (mNetworkLocationInterface != null) {
+            return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude, 
+                    lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
+                    language, country, variant, appName, addrs);
+        } else {
             return null;
         }
     }
diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java
index 40d1b72..2e430c8 100644
--- a/services/java/com/android/server/MountListener.java
+++ b/services/java/com/android/server/MountListener.java
@@ -30,7 +30,7 @@
 import java.net.Socket;
 
 /**
- * Thread for communicating with the mount service daemon via a local socket.
+ * Thread for communicating with the vol service daemon via a local socket.
  * Events received from the daemon are passed to the MountService instance, 
  * and the MountService instance calls MountListener to send commands to the daemon.
  */
@@ -38,39 +38,43 @@
 
     private static final String TAG = "MountListener";
 
-    // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/mountd/mountd.h
+    // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/vold/
     
-    // socket name for connecting to mountd
-    private static final String MOUNTD_SOCKET = "mountd";
+    // socket name for connecting to vold
+    private static final String VOLD_SOCKET = "vold";
     
-    // mountd commands
-    private static final String MOUNTD_ENABLE_UMS = "enable_ums";
-    private static final String MOUNTD_DISABLE_UMS = "disable_ums";
-    private static final String MOUNTD_SEND_STATUS = "send_status";
-    private static final String MOUNTD_MOUNT_MEDIA = "mount_media:";
-    private static final String MOUNTD_EJECT_MEDIA = "eject_media:";
+    // vold commands
+    private static final String VOLD_CMD_ENABLE_UMS = "enable_ums";
+    private static final String VOLD_CMD_DISABLE_UMS = "disable_ums";
+    private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status";
+    private static final String VOLD_CMD_MOUNT_VOLUME = "mount_volume:";
+    private static final String VOLD_CMD_EJECT_MEDIA = "eject_media:";
+    private static final String VOLD_CMD_FORMAT_MEDIA = "format_media:";
 
-    // mountd events
-    private static final String MOUNTD_UMS_ENABLED = "ums_enabled";
-    private static final String MOUNTD_UMS_DISABLED = "ums_disabled";
-    private static final String MOUNTD_UMS_CONNECTED = "ums_connected";
-    private static final String MOUNTD_UMS_DISCONNECTED = "ums_disconnected";
-    private static final String MOUNTD_MEDIA_REMOVED = "media_removed:";
-    private static final String MOUNTD_MEDIA_UNMOUNTED = "media_unmounted:";
-    private static final String MOUNTD_MEDIA_MOUNTED = "media_mounted:";
-    private static final String MOUNTD_MEDIA_MOUNTED_READ_ONLY = "media_mounted_ro:";
-    private static final String MOUNTD_MEDIA_SHARED = "media_shared:";
-    private static final String MOUNTD_MEDIA_BAD_REMOVAL = "media_bad_removal:";
-    private static final String MOUNTD_MEDIA_UNMOUNTABLE = "media_unmountable:";
-    private static final String MOUNTD_REQUEST_EJECT = "request_eject:";
+    // vold events
+    private static final String VOLD_EVT_UMS_ENABLED = "ums_enabled";
+    private static final String VOLD_EVT_UMS_DISABLED = "ums_disabled";
+    private static final String VOLD_EVT_UMS_CONNECTED = "ums_connected";
+    private static final String VOLD_EVT_UMS_DISCONNECTED = "ums_disconnected";
+
+    private static final String VOLD_EVT_NOMEDIA = "volume_nomedia:";
+    private static final String VOLD_EVT_UNMOUNTED = "volume_unmounted:";
+    private static final String VOLD_EVT_MOUNTED = "volume_mounted:";
+    private static final String VOLD_EVT_MOUNTED_RO = "volume_mounted_ro:";
+    private static final String VOLD_EVT_UMS = "volume_ums";
+    private static final String VOLD_EVT_BAD_REMOVAL = "volume_badremoval:";
+    private static final String VOLD_EVT_DAMAGED = "volume_damaged:";
+    private static final String VOLD_EVT_CHECKING = "volume_checking:";
+    private static final String VOLD_EVT_NOFS = "volume_nofs:";
+    private static final String VOLD_EVT_EJECTING = "volume_ejecting:";
 
     /**
-     * MountService that handles events received from the mount service daemon
+     * MountService that handles events received from the vol service daemon
      */
     private MountService mService;
     
     /**
-     * Stream for sending commands to the mount service daemon.
+     * Stream for sending commands to the vol service daemon.
      */
     private OutputStream mOutputStream;
     
@@ -95,9 +99,9 @@
     }
 
     /**
-     * Process and dispatches events received from the mount service daemon
+     * Process and dispatches events received from the vol service daemon
      * 
-     * @param event  An event received from the mount service daemon
+     * @param event  An event received from the vol service daemon
      */
     private void handleEvent(String event) {
         if (Config.LOGD) Log.d(TAG, "handleEvent " + event);
@@ -105,34 +109,38 @@
         int colonIndex = event.indexOf(':');
         String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null);
         
-        if (event.equals(MOUNTD_UMS_ENABLED)) {
+        if (event.equals(VOLD_EVT_UMS_ENABLED)) {
             mUmsEnabled = true;
-        } else if (event.equals(MOUNTD_UMS_DISABLED)) {
+        } else if (event.equals(VOLD_EVT_UMS_DISABLED)) {
             mUmsEnabled = false;
-        } else if (event.equals(MOUNTD_UMS_CONNECTED)) {
+        } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) {
             mUmsConnected = true;
             mService.notifyUmsConnected();
-        } else if (event.equals(MOUNTD_UMS_DISCONNECTED)) {
+        } else if (event.equals(VOLD_EVT_UMS_DISCONNECTED)) {
             mUmsConnected = false;        
             mService.notifyUmsDisconnected();
-        } else if (event.startsWith(MOUNTD_MEDIA_REMOVED)) {
+        } else if (event.startsWith(VOLD_EVT_NOMEDIA)) {
             mService.notifyMediaRemoved(path);
-        } else if (event.startsWith(MOUNTD_MEDIA_UNMOUNTED)) {
+        } else if (event.startsWith(VOLD_EVT_UNMOUNTED)) {
             mService.notifyMediaUnmounted(path);
-        } else if (event.startsWith(MOUNTD_MEDIA_MOUNTED)) {
+        } else if (event.startsWith(VOLD_EVT_CHECKING)) {
+            mService.notifyMediaChecking(path);
+        } else if (event.startsWith(VOLD_EVT_NOFS)) {
+            mService.notifyMediaNoFs(path);
+        } else if (event.startsWith(VOLD_EVT_MOUNTED)) {
             mService.notifyMediaMounted(path, false);
-        } else if (event.startsWith(MOUNTD_MEDIA_MOUNTED_READ_ONLY)) {
+        } else if (event.startsWith(VOLD_EVT_MOUNTED_RO)) {
             mService.notifyMediaMounted(path, true);
-        } else if (event.startsWith(MOUNTD_MEDIA_SHARED)) {
+        } else if (event.startsWith(VOLD_EVT_UMS)) {
             mService.notifyMediaShared(path);
-        } else if (event.startsWith(MOUNTD_MEDIA_BAD_REMOVAL)) {
+        } else if (event.startsWith(VOLD_EVT_BAD_REMOVAL)) {
             mService.notifyMediaBadRemoval(path);
             // also send media eject intent, to notify apps to close any open
             // files on the media.
             mService.notifyMediaEject(path);
-        } else if (event.startsWith(MOUNTD_MEDIA_UNMOUNTABLE)) {
+        } else if (event.startsWith(VOLD_EVT_DAMAGED)) {
             mService.notifyMediaUnmountable(path);
-        } else if (event.startsWith(MOUNTD_REQUEST_EJECT)) {
+        } else if (event.startsWith(VOLD_EVT_EJECTING)) {
             mService.notifyMediaEject(path);
         }    
     }
@@ -156,7 +164,7 @@
     private void writeCommand2(String command, String argument) {
         synchronized (this) {
             if (mOutputStream == null) {
-                Log.e(TAG, "No connection to mountd", new IllegalStateException());
+                Log.e(TAG, "No connection to vold", new IllegalStateException());
             } else {
                 StringBuilder builder = new StringBuilder(command);
                 if (argument != null) {
@@ -183,7 +191,7 @@
 
         try {
             socket = new LocalSocket();
-            LocalSocketAddress address = new LocalSocketAddress(MOUNTD_SOCKET, 
+            LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET, 
                     LocalSocketAddress.Namespace.RESERVED);
 
             socket.connect(address);
@@ -193,7 +201,7 @@
 
             byte[] buffer = new byte[100];
 
-            writeCommand(MOUNTD_SEND_STATUS);
+            writeCommand(VOLD_CMD_SEND_UMS_STATUS);
             
             while (true) {
                 int count = inputStream.read(buffer);
@@ -242,7 +250,7 @@
          * create tons of throwaway LocalSockets, making
          * system_server GC constantly.
          */
-        Log.e(TAG, "Failed to connect to mountd", new IllegalStateException());
+        Log.e(TAG, "Failed to connect to vold", new IllegalStateException());
         SystemClock.sleep(2000);
     }
 
@@ -283,7 +291,7 @@
      * @param enable  true to enable USB mass storage support
      */
     void setMassStorageEnabled(boolean enable) {
-        writeCommand(enable ? MOUNTD_ENABLE_UMS : MOUNTD_DISABLE_UMS);
+        writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS);
     }
 
     /**
@@ -297,14 +305,20 @@
      * Mount media at given mount point.
      */
     public void mountMedia(String mountPoint) {
-        writeCommand2(MOUNTD_MOUNT_MEDIA, mountPoint);
+        writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint);
     }
 
     /**
      * Unmount media at given mount point.
      */
     public void ejectMedia(String mountPoint) {
-        writeCommand2(MOUNTD_EJECT_MEDIA, mountPoint);
+        writeCommand2(VOLD_CMD_EJECT_MEDIA, mountPoint);
+    }
+
+    /**
+     * Format media at given mount point.
+     */
+    public void formatMedia(String mountPoint) {
+        writeCommand2(VOLD_CMD_FORMAT_MEDIA, mountPoint);
     }
 }
-
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 002ebed..0feb1da 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -19,15 +19,19 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.IMountService;
 import android.os.Environment;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UEventObserver;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
@@ -52,24 +56,32 @@
     private MountListener mListener;
 
     /**
-     * The notification that is shown when USB is connected. It leads the user
-     * to a dialog to enable mass storage mode.
+     * The notification that is shown when a USB mass storage host
+     * is connected. 
      * <p>
-     * This is lazily created, so use {@link #getUsbStorageNotification()}.
+     * This is lazily created, so use {@link #setUsbStorageNotification()}.
      */
     private Notification mUsbStorageNotification;
 
-    private class SdDoorListener extends UEventObserver {    
-        static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/sd-door";
-        static final String SD_DOOR_SWITCH_NAME = "sd-door";
 
-        public void onUEvent(UEvent event) {
-            if (SD_DOOR_SWITCH_NAME.equals(event.get("SWITCH_NAME"))) {
-                sdDoorStateChanged(event.get("SWITCH_STATE"));
-            }
-        }
-    };
+    /**
+     * The notification that is shown when the following media events occur:
+     *     - Media is being checked
+     *     - Media is blank (or unknown filesystem)
+     *     - Media is corrupt
+     *     - Media is safe to unmount
+     *     - Media is missing
+     * <p>
+     * This is lazily created, so use {@link #setMediaStorageNotification()}.
+     */
+    private Notification mMediaStorageNotification;
     
+    private boolean mShowSafeUnmountNotificationWhenUnmounted;
+
+    private boolean mPlaySounds;
+
+    private boolean mMounted;
+
     /**
      * Constructs a new MountService instance
      * 
@@ -77,13 +89,28 @@
      */
     public MountService(Context context) {
         mContext = context;
+
+        // Register a BOOT_COMPLETED handler so that we can start
+        // MountListener. We defer the startup so that we don't
+        // start processing events before we ought-to
+        mContext.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+
         mListener =  new MountListener(this);       
-        Thread thread = new Thread(mListener, MountListener.class.getName());
-        thread.start();
-        SdDoorListener sdDoorListener = new SdDoorListener();
-        sdDoorListener.startObserving(SdDoorListener.SD_DOOR_UEVENT_MATCH);
+        mShowSafeUnmountNotificationWhenUnmounted = false;
+
+        mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
     }
 
+    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+                Thread thread = new Thread(mListener, MountListener.class.getName());
+                thread.start();
+            }
+        }
+    };
+
     /**
      * @return true if USB mass storage support is enabled.
      */
@@ -129,19 +156,92 @@
             throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
         }
 
+        // Set a flag so that when we get the unmounted event, we know
+        // to display the notification
+        mShowSafeUnmountNotificationWhenUnmounted = true;
+
         // tell mountd to unmount the media
         mListener.ejectMedia(mountPath);
     }
 
     /**
+     * Attempt to format external media
+     */
+    public void formatMedia(String formatPath) throws RemoteException {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
+        }
+
+        mListener.formatMedia(formatPath);
+    }
+
+    /**
+     * Returns true if we're playing media notification sounds.
+     */
+    public boolean getPlayNotificationSounds() {
+        return mPlaySounds;
+    }
+
+    /**
+     * Set whether or not we're playing media notification sounds.
+     */
+    public void setPlayNotificationSounds(boolean enabled) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SETTINGS) 
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires WRITE_SETTINGS permission");
+        }
+        mPlaySounds = enabled;
+        SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
+    }
+
+    /**
+     * Update the state of the USB mass storage notification
+     */
+    void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
+
+        try {
+
+            if (getMassStorageConnected() && !suppressIfConnected) {
+                Intent intent = new Intent();
+                intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+                PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+                setUsbStorageNotification(
+                        com.android.internal.R.string.usb_storage_notification_title,
+                        com.android.internal.R.string.usb_storage_notification_message,
+                        com.android.internal.R.drawable.stat_sys_data_usb,
+                        sound, true, pi);
+            } else {
+                setUsbStorageNotification(0, 0, 0, false, false, null);
+            }
+        } catch (RemoteException e) {
+            // Nothing to do
+        }
+    }
+
+    void handlePossibleExplicitUnmountBroadcast(String path) {
+        if (mMounted) {
+            mMounted = false;
+            Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
+                    Uri.parse("file://" + path));
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    /**
      * Broadcasts the USB mass storage connected event to all clients.
      */
     void notifyUmsConnected() {
         String storageState = Environment.getExternalStorageState();
         if (!storageState.equals(Environment.MEDIA_REMOVED) &&
-                !storageState.equals(Environment.MEDIA_BAD_REMOVAL)) {
-            setUsbStorageNotificationVisibility(true);
+            !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
+            !storageState.equals(Environment.MEDIA_CHECKING)) {
+
+            updateUsbMassStorageNotification(false, true);
         }
+
         Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
         mContext.sendBroadcast(intent);
     }
@@ -150,7 +250,7 @@
      * Broadcasts the USB mass storage disconnected event to all clients.
      */
     void notifyUmsDisconnected() {
-        setUsbStorageNotificationVisibility(false);
+        updateUsbMassStorageNotification(false, false);
         Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
         mContext.sendBroadcast(intent);
     }
@@ -159,6 +259,15 @@
      * Broadcasts the media removed event to all clients.
      */
     void notifyMediaRemoved(String path) {
+        updateUsbMassStorageNotification(true, false);
+
+        setMediaStorageNotification(
+                com.android.internal.R.string.ext_media_nomedia_notification_title,
+                com.android.internal.R.string.ext_media_nomedia_notification_message,
+                com.android.internal.R.drawable.stat_sys_no_sim,
+                true, false, null);
+        handlePossibleExplicitUnmountBroadcast(path);
+
         Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
@@ -168,18 +277,68 @@
      * Broadcasts the media unmounted event to all clients.
      */
     void notifyMediaUnmounted(String path) {
+        if (mShowSafeUnmountNotificationWhenUnmounted) {
+            setMediaStorageNotification(
+                    com.android.internal.R.string.ext_media_safe_unmount_notification_title,
+                    com.android.internal.R.string.ext_media_safe_unmount_notification_message,
+                    com.android.internal.R.drawable.stat_notify_sim_toolkit,
+                    true, true, null);
+            mShowSafeUnmountNotificationWhenUnmounted = false;
+        } else {
+            setMediaStorageNotification(0, 0, 0, false, false, null);
+        }
+        updateUsbMassStorageNotification(false, false);
+
         Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
     }
 
     /**
+     * Broadcasts the media checking event to all clients.
+     */
+    void notifyMediaChecking(String path) {
+        setMediaStorageNotification(
+                com.android.internal.R.string.ext_media_checking_notification_title,
+                com.android.internal.R.string.ext_media_checking_notification_message,
+                com.android.internal.R.drawable.stat_notify_sim_toolkit,
+                true, false, null);
+
+        updateUsbMassStorageNotification(true, false);
+        Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Broadcasts the media nofs event to all clients.
+     */
+    void notifyMediaNoFs(String path) {
+        
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
+                                    com.android.internal.R.string.ext_media_nofs_notification_message,
+                                    com.android.internal.R.drawable.stat_sys_no_sim,
+                                    true, false, pi);
+        updateUsbMassStorageNotification(false, false);
+        intent = new Intent(Intent.ACTION_MEDIA_NOFS, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
      * Broadcasts the media mounted event to all clients.
      */
     void notifyMediaMounted(String path, boolean readOnly) {
+        setMediaStorageNotification(0, 0, 0, false, false, null);
+        updateUsbMassStorageNotification(false, false);
         Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                 Uri.parse("file://" + path));
         intent.putExtra("read-only", readOnly);
+        mMounted = true;
         mContext.sendBroadcast(intent);
     }
 
@@ -187,7 +346,15 @@
      * Broadcasts the media shared event to all clients.
      */
     void notifyMediaShared(String path) {
-        Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED, 
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+        setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
+                                  com.android.internal.R.string.usb_storage_stop_notification_message,
+                                  com.android.internal.R.drawable.stat_sys_warning,
+                                  false, true, pi);
+        handlePossibleExplicitUnmountBroadcast(path);
+        intent = new Intent(Intent.ACTION_MEDIA_SHARED, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
     }
@@ -196,16 +363,39 @@
      * Broadcasts the media bad removal event to all clients.
      */
     void notifyMediaBadRemoval(String path) {
+        updateUsbMassStorageNotification(true, false);
+        setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
+                                    com.android.internal.R.string.ext_media_badremoval_notification_message,
+                                    com.android.internal.R.drawable.stat_sys_warning,
+                                    true, true, null);
+
+        handlePossibleExplicitUnmountBroadcast(path);
         Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
+
+        intent = new Intent(Intent.ACTION_MEDIA_REMOVED, 
+                Uri.parse("file://" + path));
+        mContext.sendBroadcast(intent);
     }
 
     /**
      * Broadcasts the media unmountable event to all clients.
      */
     void notifyMediaUnmountable(String path) {
-        Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
+                                    com.android.internal.R.string.ext_media_unmountable_notification_message,
+                                    com.android.internal.R.drawable.stat_sys_no_sim,
+                                    true, false, pi); 
+        updateUsbMassStorageNotification(false, false);
+
+        handlePossibleExplicitUnmountBroadcast(path);
+
+        intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, 
                 Uri.parse("file://" + path));
         mContext.sendBroadcast(intent);
     }
@@ -219,90 +409,129 @@
         mContext.sendBroadcast(intent);
     }
     
-    private void sdDoorStateChanged(String doorState) {
-        File directory = Environment.getExternalStorageDirectory();
-        String storageState = Environment.getExternalStorageState();
-        
-        if (directory != null) {
-            try {
-                if (doorState.equals("open") && (storageState.equals(Environment.MEDIA_MOUNTED) ||
-                        storageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY))) {
-                    // request SD card unmount if SD card door is opened
-                    unmountMedia(directory.getPath());
-                } else if (doorState.equals("closed") && storageState.equals(Environment.MEDIA_UNMOUNTED)) {
-                    // attempt to remount SD card
-                    mountMedia(directory.getPath());
-                }
-            } catch (RemoteException e) {
-                // Nothing to do.
-            }
-        }
-    }
-
     /**
-     * Sets the visibility of the USB storage notification. This should be
-     * called when a USB cable is connected and also when it is disconnected.
-     * 
-     * @param visible Whether to show or hide the notification.
+     * Sets the USB storage notification.
      */
-    private void setUsbStorageNotificationVisibility(boolean visible) {
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager == null) {
+    private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
+                                                        PendingIntent pi) {
+
+        if (!visible && mUsbStorageNotification == null) {
             return;
         }
 
-        /*
-         * The convention for notification IDs is to use the icon's resource ID
-         * when the icon is only used by a single notification type, which is
-         * the case here.
-         */
-        Notification notification = getUsbStorageNotification();
-        final int notificationId = notification.icon;
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (notificationManager == null) {
+            return;
+        }
         
         if (visible) {
-            notificationManager.notify(notificationId, notification);
+            Resources r = Resources.getSystem();
+            CharSequence title = r.getText(titleId);
+            CharSequence message = r.getText(messageId);
+
+            if (mUsbStorageNotification == null) {
+                mUsbStorageNotification = new Notification();
+                mUsbStorageNotification.icon = icon;
+                mUsbStorageNotification.when = 0;
+            }
+
+            if (sound && mPlaySounds) {
+                mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+            } else {
+                mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
+            }
+                
+            mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+
+            mUsbStorageNotification.tickerText = title;
+            if (pi == null) {
+                Intent intent = new Intent();
+                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            }
+
+            mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+        }
+    
+        final int notificationId = mUsbStorageNotification.icon;
+        if (visible) {
+            notificationManager.notify(notificationId, mUsbStorageNotification);
         } else {
             notificationManager.cancel(notificationId);
         }
     }
 
-    /**
-     * Gets the USB storage notification.
-     * 
-     * @return A {@link Notification} that leads to the dialog to enable USB storage.
-     */
-    private synchronized Notification getUsbStorageNotification() {
-        Resources r = Resources.getSystem();
-        CharSequence title =
-                r.getText(com.android.internal.R.string.usb_storage_notification_title);
-        CharSequence message =
-                r.getText(com.android.internal.R.string.usb_storage_notification_message);
+    private synchronized boolean getMediaStorageNotificationDismissable() {
+        if ((mMediaStorageNotification != null) &&
+            ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
+                    Notification.FLAG_AUTO_CANCEL))
+            return true;
 
-        if (mUsbStorageNotification == null) {
-            mUsbStorageNotification = new Notification();
-            mUsbStorageNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
-            mUsbStorageNotification.when = 0;
-            mUsbStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
-            mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+        return false;
+    }
+
+    /**
+     * Sets the media storage notification.
+     */
+    private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
+                                                          boolean dismissable, PendingIntent pi) {
+
+        if (!visible && mMediaStorageNotification == null) {
+            return;
         }
 
-        mUsbStorageNotification.tickerText = title;
-        mUsbStorageNotification.setLatestEventInfo(mContext, title, message,
-                getUsbStorageDialogIntent());
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
 
-        return mUsbStorageNotification;
-    }
+        if (notificationManager == null) {
+            return;
+        }
+
+        if (mMediaStorageNotification != null && visible) {
+            /*
+             * Dismiss the previous notification - we're about to
+             * re-use it.
+             */
+            final int notificationId = mMediaStorageNotification.icon;
+            notificationManager.cancel(notificationId);
+        }
+        
+        if (visible) {
+            Resources r = Resources.getSystem();
+            CharSequence title = r.getText(titleId);
+            CharSequence message = r.getText(messageId);
+
+            if (mMediaStorageNotification == null) {
+                mMediaStorageNotification = new Notification();
+                mMediaStorageNotification.when = 0;
+                if (mPlaySounds) {
+                    mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
+                }
+            }
+
+            if (dismissable) {
+                mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
+            } else {
+                mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
+            }
+
+            mMediaStorageNotification.tickerText = title;
+            if (pi == null) {
+                Intent intent = new Intent();
+                pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            }
+
+            mMediaStorageNotification.icon = icon;
+            mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
+        }
     
-    /**
-     * Creates a pending intent to start the USB storage activity.
-     * 
-     * @return A {@link PendingIntent} that start the USB storage activity.
-     */
-    private PendingIntent getUsbStorageDialogIntent() {
-        Intent intent = new Intent();
-        intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
-        return PendingIntent.getActivity(mContext, 0, intent, 0);
+        final int notificationId = mMediaStorageNotification.icon;
+        if (visible) {
+            notificationManager.notify(notificationId, mMediaStorageNotification);
+        } else {
+            notificationManager.cancel(notificationId);
+        }
     }
 }
 
diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java
index 11dbe63..1ea0bac 100644
--- a/services/java/com/android/server/NetStatService.java
+++ b/services/java/com/android/server/NetStatService.java
@@ -26,20 +26,35 @@
 
     }
 
-    public int getTxPackets() {
-        return NetStat.netStatGetTxPkts();
+    public long getMobileTxPackets() {
+        return NetStat.getMobileTxPkts();
     }
 
-    public int getRxPackets() {
-        return NetStat.netStatGetRxPkts();
+    public long getMobileRxPackets() {
+        return NetStat.getMobileRxPkts();
     }
 
-    public int getTxBytes() {
-        return NetStat.netStatGetTxBytes();
+    public long getMobileTxBytes() {
+        return NetStat.getMobileTxBytes();
     }
 
-    public int getRxBytes() {
-        return NetStat.netStatGetRxBytes();
+    public long getMobileRxBytes() {
+        return NetStat.getMobileRxBytes();
     }
 
+    public long getTotalTxPackets() {
+        return NetStat.getTotalTxPkts();
+    }
+
+    public long getTotalRxPackets() {
+        return NetStat.getTotalRxPkts();
+    }
+
+    public long getTotalTxBytes() {
+        return NetStat.getTotalTxBytes();
+    }
+
+    public long getTotalRxBytes() {
+        return NetStat.getTotalRxBytes();
+    }
 }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index eb9ebe9..e5de7f9 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -865,7 +865,7 @@
     // ======================================================================
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission("android.permission.DUMP")
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump NotificationManager from from pid="
                     + Binder.getCallingPid()
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 58ad426..221ba46 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -97,6 +97,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
@@ -983,6 +984,20 @@
         }
         return null;
     }
+    
+    public String[] getSystemSharedLibraryNames() {
+        Set<String> libSet;
+        synchronized (mPackages) {
+            libSet = mSharedLibraries.keySet();
+        }
+        int size = libSet.size();
+        if (size > 0) {
+            String[] libs = new String[size];
+            libSet.toArray(libs);
+            return libs;
+        }
+        return null;
+    }
 
     public int checkPermission(String permName, String pkgName) {
         synchronized (mPackages) {
@@ -2678,7 +2693,7 @@
             grantPermissionsLP(pkg, false);
         }
     }
-
+    
     private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) {
         final PackageSetting ps = (PackageSetting)pkg.mExtras;
         if (ps == null) {
@@ -2724,7 +2739,19 @@
                                     == PackageManager.SIGNATURE_MATCH);
                     if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
                         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
-                            allowed = true;
+                            // For updated system applications, the signatureOrSystem permission
+                            // is granted only if it had been defined by the original application.
+                            if ((pkg.applicationInfo.flags 
+                                    & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)  != 0) {
+                                PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName);
+                                if(sysPs.grantedPermissions.contains(perm)) {
+                                    allowed = true;
+                                } else {
+                                    allowed = false;
+                                }
+                            } else {
+                                allowed = true;
+                            }
                         }
                     }
                 } else {
@@ -3157,6 +3184,7 @@
             if (removedPackage != null) {
                 Bundle extras = new Bundle(1);
                 extras.putInt(Intent.EXTRA_UID, removedUid);
+                extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras);
             }
             if (addedPackage != null) {
@@ -3194,7 +3222,7 @@
                 // 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();
+                    res.removedInfo.sendBroadcast(false, true);
                     Bundle extras = new Bundle(1);
                     extras.putInt(Intent.EXTRA_UID, res.uid);
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
@@ -3842,7 +3870,7 @@
         }
         
         if(res && sendBroadCast) {
-            info.sendBroadcast();
+            info.sendBroadcast(deleteCodeAndResources, false);
         }
         return res;
     }
@@ -3852,9 +3880,13 @@
         int uid = -1;
         int removedUid = -1;
         
-        void sendBroadcast() {
+        void sendBroadcast(boolean fullRemove, boolean replacing) {
             Bundle extras = new Bundle(1);
             extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid);
+            extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
+            if (replacing) {
+                extras.putBoolean(Intent.EXTRA_REPLACING, true);
+            }
             if (removedPackage != null) {
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras);
             }
@@ -4463,6 +4495,10 @@
         mSystemReady = true;
     }
 
+    public boolean isSafeMode() {
+        return mSafeMode;
+    }
+
     public boolean hasSystemUidErrors() {
         return mHasSystemUidErrors;
     }
@@ -4482,7 +4518,7 @@
     
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump ActivityManager from from pid="
                     + Binder.getCallingPid()
@@ -5637,23 +5673,8 @@
                 }
                 
                 for (PackageSetting pkg : mDisabledSysPackages.values()) {
-                    serializer.startTag(null, "updated-package");
-                    serializer.attribute(null, "name", pkg.name);
-                    serializer.attribute(null, "codePath", pkg.codePathString);
-                    serializer.attribute(null, "ts", pkg.getTimeStampStr());
-                    if (!pkg.resourcePathString.equals(pkg.codePathString)) {
-                        serializer.attribute(null, "resourcePath", pkg.resourcePathString);
-                    }
-                    if (pkg.sharedUser == null) {
-                        serializer.attribute(null, "userId",
-                                Integer.toString(pkg.userId));
-                    } else {
-                        serializer.attribute(null, "sharedUserId",
-                                Integer.toString(pkg.userId));
-                    }
-                    serializer.endTag(null, "updated-package");
+                    writeDisabledSysPackage(serializer, pkg);
                 }
-                
 
                 serializer.startTag(null, "preferred-packages");
                 int N = mPreferredPackages.size();
@@ -5716,6 +5737,43 @@
             //Debug.stopMethodTracing();
         }
        
+        void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) 
+        throws java.io.IOException {
+            serializer.startTag(null, "updated-package");
+            serializer.attribute(null, "name", pkg.name);
+            serializer.attribute(null, "codePath", pkg.codePathString);
+            serializer.attribute(null, "ts", pkg.getTimeStampStr());
+            if (!pkg.resourcePathString.equals(pkg.codePathString)) {
+                serializer.attribute(null, "resourcePath", pkg.resourcePathString);
+            }
+            if (pkg.sharedUser == null) {
+                serializer.attribute(null, "userId",
+                        Integer.toString(pkg.userId));
+            } else {
+                serializer.attribute(null, "sharedUserId",
+                        Integer.toString(pkg.userId));
+            }
+            serializer.startTag(null, "perms");
+            if (pkg.sharedUser == null) {
+                // If this is a shared user, the permissions will
+                // be written there.  We still need to write an
+                // empty permissions list so permissionsFixed will
+                // be set.
+                for (final String name : pkg.grantedPermissions) {
+                    BasePermission bp = mPermissions.get(name);
+                    if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) {
+                        // We only need to write signature or system permissions but this wont
+                        // match the semantics of grantedPermissions. So write all permissions.
+                        serializer.startTag(null, "item");
+                        serializer.attribute(null, "name", name);
+                        serializer.endTag(null, "item");
+                    }
+                }
+            }
+            serializer.endTag(null, "perms");
+            serializer.endTag(null, "updated-package");
+        }
+        
         void writePackage(XmlSerializer serializer, final PackageSetting pkg) 
         throws java.io.IOException {
             serializer.startTag(null, "package");
@@ -5892,33 +5950,7 @@
                     } else if (tagName.equals("preferred-activities")) {
                         readPreferredActivitiesLP(parser);
                     } else if(tagName.equals("updated-package")) {
-                        String name = parser.getAttributeValue(null, "name");
-                        String codePathStr = parser.getAttributeValue(null, "codePath");
-                        String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
-                        if(resourcePathStr == null) {
-                            resourcePathStr = codePathStr;
-                        }
-                        
-                        int pkgFlags = 0;
-                        pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
-                        PackageSetting ps = new PackageSetting(name, 
-                                new File(codePathStr), 
-                                new File(resourcePathStr), pkgFlags);
-                        String timeStampStr = parser.getAttributeValue(null, "ts");
-                        if (timeStampStr != null) {
-                            try {
-                                long timeStamp = Long.parseLong(timeStampStr);
-                                ps.setTimeStamp(timeStamp, timeStampStr);
-                            } catch (NumberFormatException e) {
-                            }
-                        }
-                        String idStr = parser.getAttributeValue(null, "userId");
-                        ps.userId = idStr != null ? Integer.parseInt(idStr) : 0;
-                        if(ps.userId <= 0) {
-                            String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
-                            ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
-                        }
-                        mDisabledSysPackages.put(name, ps);
+                        readDisabledSysPackageLP(parser);
                     } else {
                         Log.w(TAG, "Unknown element under <packages>: "
                               + parser.getName());
@@ -6056,6 +6088,58 @@
             }
         }
         
+        private void readDisabledSysPackageLP(XmlPullParser parser)
+        throws XmlPullParserException, IOException {
+            String name = parser.getAttributeValue(null, "name");
+            String codePathStr = parser.getAttributeValue(null, "codePath");
+            String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+            if(resourcePathStr == null) {
+                resourcePathStr = codePathStr;
+            }
+            
+            int pkgFlags = 0;
+            pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+            PackageSetting ps = new PackageSetting(name, 
+                    new File(codePathStr), 
+                    new File(resourcePathStr), pkgFlags);
+            String timeStampStr = parser.getAttributeValue(null, "ts");
+            if (timeStampStr != null) {
+                try {
+                    long timeStamp = Long.parseLong(timeStampStr);
+                    ps.setTimeStamp(timeStamp, timeStampStr);
+                } catch (NumberFormatException e) {
+                }
+            }
+            String idStr = parser.getAttributeValue(null, "userId");
+            ps.userId = idStr != null ? Integer.parseInt(idStr) : 0;
+            if(ps.userId <= 0) {
+                String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
+                ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
+            }
+            int outerDepth = parser.getDepth();
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                   && (type != XmlPullParser.END_TAG
+                           || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG
+                        || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                String tagName = parser.getName();
+                if (tagName.equals("perms")) {
+                    readGrantedPermissionsLP(parser,
+                            ps.grantedPermissions);
+                } else {
+                    reportSettingsProblem(Log.WARN,
+                            "Unknown element under <updated-package>: "
+                            + parser.getName());
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            }
+            mDisabledSysPackages.put(name, ps);
+        }
+        
         private void readPackageLP(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             String name = null;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index ca0ad1a..7c111d3 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -486,8 +486,13 @@
 
     public void acquireWakeLock(int flags, IBinder lock, String tag) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
-        synchronized (mLocks) {
-            acquireWakeLockLocked(flags, lock, tag);
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLocks) {
+                acquireWakeLockLocked(flags, lock, tag);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -572,12 +577,13 @@
         }
 
         if (acquireType >= 0) {
+            long origId = Binder.clearCallingIdentity();
             try {
-                long origId = Binder.clearCallingIdentity();
                 mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
-                Binder.restoreCallingIdentity(origId);
             } catch (RemoteException e) {
                 // Ignore
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
         }
     }
@@ -627,12 +633,13 @@
         releaseType = wl.monitorType;
 
         if (releaseType >= 0) {
+            long origId = Binder.clearCallingIdentity();
             try {
-                long origId = Binder.clearCallingIdentity();
                 mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
-                Binder.restoreCallingIdentity(origId);
             } catch (RemoteException e) {
                 // Ignore
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
         }
     }
@@ -757,7 +764,7 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump PowerManager from from pid="
                     + Binder.getCallingPid()
@@ -960,8 +967,10 @@
                     Log.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock);
                 }
                 if (mContext != null) {
-                    mContext.sendOrderedBroadcast(mScreenOnIntent, null,
-                            mScreenOnBroadcastDone, mHandler, 0, null, null);
+                    if (ActivityManagerNative.isSystemReady()) {
+                        mContext.sendOrderedBroadcast(mScreenOnIntent, null,
+                                mScreenOnBroadcastDone, mHandler, 0, null, null);
+                    }
                 } else {
                     synchronized (mLocks) {
                         EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 2,
@@ -1232,6 +1241,14 @@
                     }
                     if (reallyTurnScreenOn) {
                         err = Power.setScreenState(true);
+                        long identity = Binder.clearCallingIdentity();
+                        try {
+                            mBatteryStats.noteScreenOn();
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
                     } else {
                         Power.setScreenState(false);
                         // But continue as if we really did turn the screen on...
@@ -1250,6 +1267,14 @@
                     }
                 } else {
                     mScreenOffTime = SystemClock.elapsedRealtime();
+                    long identity = Binder.clearCallingIdentity();
+                    try {
+                        mBatteryStats.noteScreenOff();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e);
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
                     if (!mScreenBrightness.animating) {
                         err = turnScreenOffLocked(becauseOfUser);
                     } else {
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
index f56088c..461b006 100644
--- a/services/java/com/android/server/SensorService.java
+++ b/services/java/com/android/server/SensorService.java
@@ -105,17 +105,17 @@
         return _sensors_control_open();
     }
     
-    public boolean enableSensor(IBinder binder, int sensor, int enable)
+    public boolean enableSensor(IBinder binder, String name, int sensor, int enable)
              throws RemoteException {
-        if (localLOGV) Log.d(TAG, "enableSensor " + sensor + " " + enable);
+        if (localLOGV) Log.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
         
         // Inform battery statistics service of status change
         int uid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         if (enable == SENSOR_DISABLE) {
-            mBatteryStats.noteStopSensor(uid, sensor);
+            mBatteryStats.noteStopSensor(uid, name, sensor);
         } else {
-            mBatteryStats.noteStartSensor(uid, sensor);
+            mBatteryStats.noteStartSensor(uid, name, sensor);
         }
         Binder.restoreCallingIdentity(identity);
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7f7a52e..d624573 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,35 +16,36 @@
 
 package com.android.server;
 
+import com.android.server.am.ActivityManagerService;
+import com.android.server.status.StatusBarService;
+
+import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
+
 import android.app.ActivityManagerNative;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentService;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.media.AudioService;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.IBinder;
-import android.provider.Settings;
 import android.provider.Contacts.People;
-import android.server.BluetoothDeviceService;
+import android.provider.Settings;
 import android.server.BluetoothA2dpService;
-import android.server.checkin.FallbackCheckinService;
+import android.server.BluetoothDeviceService;
 import android.server.search.SearchManagerService;
 import android.util.EventLog;
 import android.util.Log;
 
-import dalvik.system.TouchDex;
-import dalvik.system.VMRuntime;
-
-import com.android.server.am.ActivityManagerService;
-import com.android.server.status.StatusBarService;
-
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 
@@ -180,7 +181,7 @@
 
         StatusBarService statusBar = null;
         InputMethodManagerService imm = null;
-        
+
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
             try {
                 Log.i(TAG, "Starting Status Bar Service.");
@@ -205,7 +206,7 @@
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Input Manager Service", e);
             }
-            
+
             try {
                 Log.i(TAG, "Starting Hardware Service.");
                 ServiceManager.addService("hardware", new HardwareService(context));
@@ -272,9 +273,14 @@
             }
 
             try {
-                Log.i(TAG, "Starting Checkin Service");
-                addService(context, "checkin", "com.google.android.server.checkin.CheckinService",
-                        FallbackCheckinService.class);
+                Log.i(TAG, "Starting Checkin Service.");
+                Intent intent = new Intent().setComponent(new ComponentName(
+                        "com.google.android.server.checkin",
+                        "com.google.android.server.checkin.CheckinService"));
+                if (context.startService(intent) == null) {
+                    Log.w(TAG, "Using fallback Checkin Service.");
+                    ServiceManager.addService("checkin", new FallbackCheckinService(context));
+                }
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Checkin Service", e);
             }
@@ -318,6 +324,7 @@
                 false, new AdbSettingsObserver());
 
         // It is now time to start up the app processes...
+        boolean safeMode = wm.detectSafeMode();
         if (statusBar != null) {
             statusBar.systemReady();
         }
@@ -342,51 +349,6 @@
         Looper.loop();
         Log.d(TAG, "System ServerThread is exiting!");
     }
-
-    private void addService(Context context, String name, String serviceClass,
-            Class<? extends IBinder> fallback) {
-
-        final IBinder service = findService(context, serviceClass, fallback);
-        if (service != null) {
-            ServiceManager.addService(name, service);
-        } else {
-            Log.e(TAG, "Failure starting service '" + name + "' with class " + serviceClass);
-        }
-    }
-
-    private IBinder findService(Context context, String serviceClass,
-            Class<? extends IBinder> fallback) {
-
-        IBinder service = null;
-        try {
-            Class<?> klass = Class.forName(serviceClass);
-            Constructor<?> c = klass.getConstructor(Context.class);
-            service = (IBinder) c.newInstance(context);
-        } catch (ClassNotFoundException e) {
-            // Ignore
-        } catch (IllegalAccessException e) {
-            // Ignore
-        } catch (NoSuchMethodException e) {
-            // Ignore
-        } catch (InvocationTargetException e) {
-            // Ignore
-        } catch (InstantiationException e) {
-            // Ignore
-        }
-
-        if (service == null && fallback != null) {
-            Log.w(TAG, "Could not find " + serviceClass + ", trying fallback");
-            try {
-                service = fallback.newInstance();
-            } catch (IllegalAccessException e) {
-                // Ignore
-            } catch (InstantiationException e) {
-                // Ignore
-            }
-        }
-
-        return service;
-    }
 }
 
 class DemoThread extends Thread
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index dbaf086..b5cf1aa 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -363,7 +363,7 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump telephony.registry from from pid="
                     + Binder.getCallingPid()
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 2d61b1e..c009fac 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -22,6 +22,7 @@
 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
 
+import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -204,8 +205,6 @@
                         mWifiHandler.removeMessages(MESSAGE_RELEASE_WAKELOCK);
                         synchronized (sDriverStopWakeLock) {
                             if (sDriverStopWakeLock.isHeld()) {
-                                if (DBG) Log.d(TAG, "Releasing driver-stop wakelock " +
-                                        sDriverStopWakeLock);
                                 sDriverStopWakeLock.release();
                             }
                         }
@@ -213,9 +212,18 @@
                 }
         );
 
-        Log.d(TAG, "WifiService starting up with Wi-Fi " +
-            (wifiEnabled ? "enabled" : "disabled"));
-        registerForBroadcasts();
+        Log.i(TAG, "WifiService starting up with Wi-Fi " +
+                (wifiEnabled ? "enabled" : "disabled"));
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        updateWifiState();
+                    }
+                },
+                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
+
         setWifiEnabledBlocking(wifiEnabled, false);
     }
 
@@ -293,7 +301,7 @@
                         decrementHiddentNetworkPresentCounter();
                     }
                 }
-                mIsHiddenNetworkPresent.put(netId, new Boolean(present));
+                mIsHiddenNetworkPresent.put(netId, present);
             }
         } else {
             Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!");
@@ -422,6 +430,7 @@
 
     /**
      * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
+     * @param enable {@code true} to enable, {@code false} to disable.
      * @return {@code true} if the enable/disable operation was
      *         started or is already in the queue.
      */
@@ -429,10 +438,6 @@
         enforceChangePermission();
         if (mWifiHandler == null) return false;
 
-        /*
-         * Remove any enable/disable Wi-Fi messages we may have in the queue
-         * before adding a new one
-         */
         synchronized (mWifiHandler) {
             sWakeLock.acquire();
             sendEnableMessage(enable, true);
@@ -458,40 +463,42 @@
             return false;
         }
 
-        updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING);
+        setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING);
 
         if (enable) {
             if (!WifiNative.loadDriver()) {
                 Log.e(TAG, "Failed to load Wi-Fi driver.");
-                updateWifiState(WIFI_STATE_UNKNOWN);
+                setWifiEnabledState(WIFI_STATE_UNKNOWN);
                 return false;
             }
             if (!WifiNative.startSupplicant()) {
                 WifiNative.unloadDriver();
                 Log.e(TAG, "Failed to start supplicant daemon.");
-                updateWifiState(WIFI_STATE_UNKNOWN);
+                setWifiEnabledState(WIFI_STATE_UNKNOWN);
                 return false;
             }
+            registerForBroadcasts();
             mWifiStateTracker.startEventLoop();
         } else {
 
-            // Remove notification (it will no-op if it isn't visible)
+            mContext.unregisterReceiver(mReceiver);
+           // Remove notification (it will no-op if it isn't visible)
             mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
 
             boolean failedToStopSupplicantOrUnloadDriver = false;
             if (!WifiNative.stopSupplicant()) {
                 Log.e(TAG, "Failed to stop supplicant daemon.");
-                updateWifiState(WIFI_STATE_UNKNOWN);
+                setWifiEnabledState(WIFI_STATE_UNKNOWN);
                 failedToStopSupplicantOrUnloadDriver = true;
             }
-            
+
             // We must reset the interface before we unload the driver
             mWifiStateTracker.resetInterface();
-            
+
             if (!WifiNative.unloadDriver()) {
                 Log.e(TAG, "Failed to unload Wi-Fi driver.");
                 if (!failedToStopSupplicantOrUnloadDriver) {
-                    updateWifiState(WIFI_STATE_UNKNOWN);
+                    setWifiEnabledState(WIFI_STATE_UNKNOWN);
                     failedToStopSupplicantOrUnloadDriver = true;
                 }
             }
@@ -505,7 +512,7 @@
         if (persist) {
             persistWifiEnabled(enable);
         }
-        updateWifiState(eventualWifiState);
+        setWifiEnabledState(eventualWifiState);
 
         /*
          * Initialize the hidden networks state and the number of allowed
@@ -519,7 +526,7 @@
         return true;
     }
 
-    private void updateWifiState(int wifiState) {
+    private void setWifiEnabledState(int wifiState) {
         final int previousWifiState = mWifiState;
 
         // Update state
@@ -527,6 +534,7 @@
 
         // Broadcast
         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
         mContext.sendStickyBroadcast(intent);
@@ -551,7 +559,7 @@
      *         {@link WifiManager#WIFI_STATE_ENABLING},
      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
      */
-    public int getWifiState() {
+    public int getWifiEnabledState() {
         enforceAccessPermission();
         return mWifiState;
     }
@@ -1082,9 +1090,7 @@
          */
         removeNetworkIfHidden(netId);
 
-        synchronized (mWifiStateTracker) {
-            return WifiNative.removeNetworkCommand(netId);
-        }
+        return mWifiStateTracker.removeNetwork(netId);
     }
 
     /**
@@ -1221,7 +1227,6 @@
                             if (scanResult != null) {
                               scanResult.level = -scanResultLevel;
                               scanList.add(scanResult);
-                              //if (DBG) Log.d(TAG, "ScanResult: " + scanResult);
                             }
                         } else if (DBG) {
                             Log.w(TAG,
@@ -1344,7 +1349,7 @@
             if (result && mNeedReconfig) {
                 mNeedReconfig = false;
                 result = WifiNative.reloadConfigCommand();
-                
+
                 if (result) {
                     Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
                     mContext.sendBroadcast(intent);
@@ -1446,9 +1451,7 @@
             int stayAwakeConditions =
                     Settings.System.getInt(mContext.getContentResolver(),
                                            Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
-            if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
-                /* do nothing, we'll check isAirplaneModeOn later. */
-            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
+            if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 mAlarmManager.cancel(mIdleIntent);
                 mDeviceIdle = false;
                 mScreenOff = false;
@@ -1543,7 +1546,6 @@
     }
 
     private void sendStartMessage(boolean scanOnlyMode) {
-        if (DBG) Log.d(TAG, "sendStartMessage(" + scanOnlyMode + ")");
         Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
     }
 
@@ -1561,6 +1563,9 @@
         }
 
         synchronized (mWifiHandler) {
+            if (mWifiState == WIFI_STATE_ENABLING && !airplaneMode) {
+                return;
+            }
             if (wifiShouldBeEnabled) {
                 if (wifiShouldBeStarted) {
                     sWakeLock.acquire();
@@ -1573,6 +1578,15 @@
                                     mContext.getContentResolver(),
                                     Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                                     DEFAULT_WAKELOCK_TIMEOUT);
+                    /*
+                     * The following wakelock is held in order to ensure
+                     * that the connectivity manager has time to fail over
+                     * to the mobile data network. The connectivity manager
+                     * releases it once mobile data connectivity has been
+                     * established. If connectivity cannot be established,
+                     * the wakelock is released after wakeLockTimeout
+                     * milliseconds have elapsed.
+                     */
                     sDriverStopWakeLock.acquire();
                     mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
                     mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
@@ -1585,18 +1599,15 @@
     }
 
     private void registerForBroadcasts() {
+        IntentFilter intentFilter = new IntentFilter();
         if (isAirplaneSensitive()) {
-            mContext.registerReceiver(mReceiver,
-                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
+            intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         }
-        mContext.registerReceiver(mReceiver,
-                new IntentFilter(Intent.ACTION_SCREEN_ON));
-        mContext.registerReceiver(mReceiver,
-                new IntentFilter(Intent.ACTION_SCREEN_OFF));
-        mContext.registerReceiver(mReceiver,
-                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-        mContext.registerReceiver(mReceiver,
-                new IntentFilter(ACTION_DEVICE_IDLE));
+        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        intentFilter.addAction(ACTION_DEVICE_IDLE);
+        mContext.registerReceiver(mReceiver, intentFilter);
     }
     
     private boolean isAirplaneSensitive() {
@@ -1664,7 +1675,7 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump WifiService from from pid="
                     + Binder.getCallingPid()
@@ -1672,11 +1683,14 @@
             return;
         }
         pw.println("Wi-Fi is " + stateName(mWifiState));
-        pw.println("stay-awake conditions: " +
+        pw.println("Stay-awake conditions: " +
                 Settings.System.getInt(mContext.getContentResolver(),
                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
         pw.println();
 
+        pw.println("Internal state:");
+        pw.println(mWifiStateTracker);
+        pw.println();
         pw.println("Latest scan results:");
         List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
         if (scanResults != null && scanResults.size() != 0) {
@@ -1786,13 +1800,6 @@
             return -1;
         }
 
-        private synchronized void clear() {
-            if (!mList.isEmpty()) {
-                mList.clear();
-                updateWifiState();
-            }
-        }
-
         private void dump(PrintWriter pw) {
             for (WifiLock l : mList) {
                 pw.print("    ");
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 7ed0aec..09f5d8f 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -122,7 +122,9 @@
     static final boolean DEBUG = false;
     static final boolean DEBUG_FOCUS = false;
     static final boolean DEBUG_ANIM = false;
+    static final boolean DEBUG_LAYERS = false;
     static final boolean DEBUG_INPUT = false;
+    static final boolean DEBUG_INPUT_METHOD = false;
     static final boolean DEBUG_VISIBILITY = false;
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_TRANSITIONS = false;
@@ -202,6 +204,8 @@
 
     final boolean mHaveInputMethods;
     
+    final boolean mLimitedAlphaCompositing;
+    
     final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
 
     final IActivityManager mActivityManager;
@@ -306,6 +310,7 @@
     
     final float[] mTmpFloats = new float[9];
 
+    boolean mSafeMode;
     boolean mDisplayEnabled = false;
     boolean mSystemBooted = false;
     int mRotation = 0;
@@ -316,7 +321,6 @@
     
     boolean mLayoutNeeded = true;
     boolean mAnimationPending = false;
-    boolean mSurfacesChanged = false;
     boolean mDisplayFrozen = false;
     boolean mWindowsFreezingScreen = false;
     long mFreezeGcPending = 0;
@@ -347,6 +351,8 @@
     // This just indicates the window the input method is on top of, not
     // necessarily the window its input is going to.
     WindowState mInputMethodTarget = null;
+    boolean mInputMethodTargetWaitingAnim;
+    int mInputMethodAnimLayerAdjustment;
     
     WindowState mInputMethodWindow = null;
     final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
@@ -461,6 +467,8 @@
             boolean haveInputMethods) {
         mContext = context;
         mHaveInputMethods = haveInputMethods;
+        mLimitedAlphaCompositing = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_limitedAlpha);
         
         mPowerManager = pm;
         mPowerManager.setPolicy(mPolicy);
@@ -734,7 +742,7 @@
         }
     }
     
-    int findDesiredInputMethodWindowIndexLocked() {
+    int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
         final ArrayList localmWindows = mWindows;
         final int N = localmWindows.size();
         WindowState w = null;
@@ -753,17 +761,67 @@
                 }
             }
         }
+        
+        if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target="
+                + w + " willMove=" + willMove);
+        
+        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);
+                }
+                if (curIndex >= 0) {
+                    return curIndex + 1;
+                }
+            }
+        }
+        
         //Log.i(TAG, "Placing input method @" + (i+1));
         if (w != null) {
-            mInputMethodTarget = w;
+            if (willMove) {
+                RuntimeException e = new RuntimeException();
+                e.fillInStackTrace();
+                if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+                        + mInputMethodTarget + " to " + w, e);
+                mInputMethodTarget = w;
+                if (w.mAppToken != null) {
+                    setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
+                } else {
+                    setInputMethodAnimLayerAdjustment(0);
+                }
+            }
             return i+1;
         }
-        mInputMethodTarget = null;
+        if (willMove) {
+            RuntimeException e = new RuntimeException();
+            e.fillInStackTrace();
+            if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from "
+                    + mInputMethodTarget + " to null", e);
+            mInputMethodTarget = null;
+            setInputMethodAnimLayerAdjustment(0);
+        }
         return -1;
     }
     
     void addInputMethodWindowToListLocked(WindowState win) {
-        int pos = findDesiredInputMethodWindowIndexLocked();
+        int pos = findDesiredInputMethodWindowIndexLocked(true);
         if (pos >= 0) {
             win.mTargetAppToken = mInputMethodTarget.mAppToken;
             mWindows.add(pos, win);
@@ -775,6 +833,33 @@
         moveInputMethodDialogsLocked(pos);
     }
     
+    void setInputMethodAnimLayerAdjustment(int adj) {
+        if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj);
+        mInputMethodAnimLayerAdjustment = adj;
+        WindowState imw = mInputMethodWindow;
+        if (imw != null) {
+            imw.mAnimLayer = imw.mLayer + adj;
+            if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw
+                    + " anim layer: " + imw.mAnimLayer);
+            int wi = imw.mChildWindows.size();
+            while (wi > 0) {
+                wi--;
+                WindowState cw = (WindowState)imw.mChildWindows.get(wi);
+                cw.mAnimLayer = cw.mLayer + adj;
+                if (DEBUG_LAYERS) Log.v(TAG, "IM win " + cw
+                        + " anim layer: " + cw.mAnimLayer);
+            }
+        }
+        int di = mInputMethodDialogs.size();
+        while (di > 0) {
+            di --;
+            imw = mInputMethodDialogs.get(di);
+            imw.mAnimLayer = imw.mLayer + adj;
+            if (DEBUG_LAYERS) Log.v(TAG, "IM win " + imw
+                    + " anim layer: " + imw.mAnimLayer);
+        }
+    }
+    
     private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
         int wpos = mWindows.indexOf(win);
         if (wpos >= 0) {
@@ -814,9 +899,11 @@
         }
         if (pos >= 0) {
             final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
-            WindowState wp = (WindowState)mWindows.get(pos);
-            if (wp == mInputMethodWindow) {
-                pos++;
+            if (pos < mWindows.size()) {
+                WindowState wp = (WindowState)mWindows.get(pos);
+                if (wp == mInputMethodWindow) {
+                    pos++;
+                }
             }
             for (int i=0; i<N; i++) {
                 WindowState win = dialogs.get(i);
@@ -839,7 +926,7 @@
             return false;
         }
         
-        int imPos = findDesiredInputMethodWindowIndexLocked();
+        int imPos = findDesiredInputMethodWindowIndexLocked(true);
         if (imPos >= 0) {
             // In this case, the input method windows are to be placed
             // immediately above the window they are targeting.
@@ -915,7 +1002,7 @@
     }
     
     void adjustInputMethodDialogsLocked() {
-        moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked());
+        moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
     }
     
     public int addWindow(Session session, IWindow client,
@@ -975,7 +1062,7 @@
                           + attrs.token + ".  Aborting.");
                     return WindowManagerImpl.ADD_BAD_APP_TOKEN;
                 }
-                token = new WindowToken(attrs.token, -1);
+                token = new WindowToken(attrs.token, -1, false);
                 addToken = true;
             } else if (attrs.type >= FIRST_APPLICATION_WINDOW
                     && attrs.type <= LAST_APPLICATION_WINDOW) {
@@ -1087,11 +1174,23 @@
                 + ": window=" + win);
         }
 
+        // sendNewConfiguration() checks caller permissions so we must call it with
+        // privilege.  updateOrientationFromAppTokens() clears and resets the caller
+        // identity anyway, so it's safe to just clear & restore around this whole
+        // block.
+        final long origId = Binder.clearCallingIdentity();
         if (reportNewConfig) {
-            final long origId = Binder.clearCallingIdentity();
             sendNewConfiguration();
-            Binder.restoreCallingIdentity(origId);
+        } else {
+            // Update Orientation after adding a window, only if the window needs to be
+            // displayed right away
+            if (win.isVisibleOrAdding()) {
+                if (updateOrientationFromAppTokens(null) != null) {
+                    sendNewConfiguration();
+                }
+            }
         }
+        Binder.restoreCallingIdentity(origId);
         
         return res;
     }
@@ -1125,7 +1224,8 @@
                 + " inPendingTransaction="
                 + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
                 + " mDisplayFrozen=" + mDisplayFrozen);
-        
+        // Visibility of the removed window. Will be used later to update orientation later on.
+        boolean wasVisible = false;
         // First, see if we need to run an animation.  If we do, we have
         // to hold off on removing the window until the animation is done.
         // If the display is frozen, just remove immediately, since the
@@ -1133,7 +1233,8 @@
         if (win.mSurface != null && !mDisplayFrozen) {
             // If we are not currently running the exit animation, we
             // need to see about starting one.
-            if (win.isVisibleLw()) {
+            if (wasVisible=win.isVisibleLw()) {
+                
                 int transit = WindowManagerPolicy.TRANSIT_EXIT;
                 if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
                     transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
@@ -1161,6 +1262,13 @@
         }
 
         removeWindowInnerLocked(session, win);
+        // Removing a visible window will effect the computed orientation
+        // So just update orientation if needed.
+        if (wasVisible) {
+            if (updateOrientationFromAppTokens(null) != null) {
+                sendNewConfiguration();
+            }
+        }
         updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
         Binder.restoreCallingIdentity(origId);
     }
@@ -1191,10 +1299,10 @@
                 TAG, "**** Removing window " + win + ": count="
                 + token.windows.size());
         if (token.windows.size() == 0) {
-            if (atoken != token) {
+            if (!token.explicit) {
                 mTokenMap.remove(token.token);
                 mTokenList.remove(token);
-            } else {
+            } else if (atoken != null) {
                 atoken.firstWindowDrawn = false;
             }
         }
@@ -1287,7 +1395,7 @@
             Surface outSurface) {
         boolean displayed = false;
         boolean inTouchMode;
-
+        Configuration newConfig = null;
         long origId = Binder.clearCallingIdentity();
         
         synchronized(mWindowMap) {
@@ -1435,6 +1543,7 @@
             if (assignLayers) {
                 assignLayersLocked();
             }
+            newConfig = updateOrientationFromAppTokensLocked(null);
             performLayoutAndPlaceSurfacesLocked();
             if (win.mAppToken != null) {
                 win.mAppToken.updateReportedVisibilityLocked();
@@ -1456,6 +1565,10 @@
             inTouchMode = mInTouchMode;
         }
 
+        if (newConfig != null) {
+            sendNewConfiguration();
+        }
+        
         Binder.restoreCallingIdentity(origId);
         
         return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
@@ -1708,7 +1821,7 @@
                 Log.w(TAG, "Attempted to add existing input method token: " + token);
                 return;
             }
-            wtoken = new WindowToken(token, type);
+            wtoken = new WindowToken(token, type, true);
             mTokenMap.put(token, wtoken);
             mTokenList.add(wtoken);
         }
@@ -1812,12 +1925,32 @@
         }
     }
     
-    public Configuration updateOrientationFromAppTokens(
-            IBinder freezeThisOneIfNeeded) {
-        boolean changed = false;
-        synchronized(mWindowMap) {
+    public int getOrientationFromWindowsLocked() {
+        int pos = mWindows.size() - 1;
+        while (pos >= 0) {
+            WindowState wtoken = (WindowState) mWindows.get(pos);
+            pos--;
+            if (wtoken.mAppToken != null) {
+                // We hit an application window. so the orientation will be determined by the
+                // app window. No point in continuing further.
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+            if (!wtoken.isVisibleLw()) {
+                continue;
+            }
+            int req = wtoken.mAttrs.screenOrientation;
+            if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
+                    (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+                continue;
+            } else {
+                return req;
+            }
+        }
+        return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    }
+    
+    public int getOrientationFromAppTokensLocked() {
             int pos = mAppTokens.size() - 1;
-            int req = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             int curGroup = 0;
             int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             boolean haveGroup = false;
@@ -1838,7 +1971,7 @@
                     // the orientation behind it, then we'll stick with the
                     // user's orientation.
                     if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
-                        break;
+                        return lastOrientation;
                     }
                 }
                 int or = wtoken.requestedOrientation;
@@ -1849,10 +1982,45 @@
                         or == ActivityInfo.SCREEN_ORIENTATION_SENSOR ||
                         or == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR ||
                         or == ActivityInfo.SCREEN_ORIENTATION_USER) {
-                    req = or;
-                    break;
+                    return or;
                 }
             }
+            return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    }
+    
+    public Configuration updateOrientationFromAppTokens(
+        IBinder freezeThisOneIfNeeded) {
+        Configuration config;
+        long ident = Binder.clearCallingIdentity();
+        synchronized(mWindowMap) {
+            config = updateOrientationFromAppTokensLocked(freezeThisOneIfNeeded);
+        }
+        if (config != null) {
+            mLayoutNeeded = true;
+            performLayoutAndPlaceSurfacesLocked();
+        }
+        Binder.restoreCallingIdentity(ident);
+        return config;
+    }
+    
+    /*
+     * The orientation is computed from non-application windows first. If none of
+     * the non-application windows specify orientation, the orientation is computed from
+     * application tokens. 
+     * @see android.view.IWindowManager#updateOrientationFromAppTokens(
+     * android.os.IBinder)
+     */
+    public Configuration updateOrientationFromAppTokensLocked(
+            IBinder freezeThisOneIfNeeded) {
+        boolean changed = false;
+        Configuration config = null;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            int req = getOrientationFromWindowsLocked();
+            if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+                req = getOrientationFromAppTokensLocked();
+            }
+            
             if (req != mForcedAppOrientation) {
                 changed = true;
                 mForcedAppOrientation = req;
@@ -1873,14 +2041,11 @@
                                     ActivityInfo.CONFIG_ORIENTATION);
                         }
                     }
-                    Configuration config = computeNewConfigurationLocked();
-                    if (config != null) {
-                        mLayoutNeeded = true;
-                        performLayoutAndPlaceSurfacesLocked();
-                    }
-                    return config;
+                    return computeNewConfiguration();
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
         
         return null;
@@ -2871,6 +3036,10 @@
         return KeyInputQueue.getKeycodeState(devid, sw);
     }
     
+    public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
+        return KeyInputQueue.hasKeys(keycodes, keyExists);
+    }
+    
     public void enableScreenAfterBoot() {
         synchronized(mWindowMap) {
             if (mSystemBooted) {
@@ -2987,18 +3156,19 @@
         } else {
             mRequestedRotation = rotation;
         }
-        if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from "+rotation);
-        rotation = mPolicy.rotationForOrientation(mForcedAppOrientation);
-        if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to "+rotation);
+        if (DEBUG_ORIENTATION) Log.v(TAG, "Overwriting rotation value from " + rotation);
+        rotation = mPolicy.rotationForOrientation(mForcedAppOrientation,
+                mRotation, mDisplayEnabled);
+        if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation);
         changed = mDisplayEnabled && mRotation != rotation;
         
         if (changed) {
-            mRotation = rotation;
             if (DEBUG_ORIENTATION) Log.v(TAG, 
                     "Rotation changed to " + rotation
                     + " from " + mRotation
                     + " (forceApp=" + mForcedAppOrientation
                     + ", req=" + mRequestedRotation + ")");
+            mRotation = rotation;
             mWindowsFreezingScreen = true;
             mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
             mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
@@ -3298,21 +3468,18 @@
         return null;
     }
 
+    /*
+     * Instruct the Activity Manager to fetch the current configuration and broadcast
+     * that to config-changed listeners if appropriate.
+     */
     void sendNewConfiguration() {
-        Configuration config;
-        synchronized (mWindowMap) {
-            config = computeNewConfigurationLocked();
-        }
-        
-        if (config != null) {
-            try {
-                mActivityManager.updateConfiguration(config);
-            } catch (RemoteException e) {
-            }
+        try {
+            mActivityManager.updateConfiguration(null);
+        } catch (RemoteException e) {
         }
     }
     
-    Configuration computeNewConfigurationLocked() {
+    public Configuration computeNewConfiguration() {
         synchronized (mWindowMap) {
             if (mDisplay == null) {
                 return null;
@@ -4610,6 +4777,11 @@
         }
     };
 
+    public boolean detectSafeMode() {
+        mSafeMode = mPolicy.detectSafeMode();
+        return mSafeMode;
+    }
+    
     public void systemReady() {
         mPolicy.systemReady();
     }
@@ -5639,6 +5811,11 @@
             mAnimating = false;
             mAnimation = null;
             mAnimLayer = mLayer;
+            if (mIsImWindow) {
+                mAnimLayer += mInputMethodAnimLayerAdjustment;
+            }
+            if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
+                    + " anim layer: " + mAnimLayer);
             mHasTransformation = false;
             mPolicyVisibility = mPolicyVisibilityAfterAnim;
             mTransformation.clear();
@@ -5755,7 +5932,8 @@
                 // transforming since it is more important to have that
                 // animation be smooth.
                 mShownAlpha = mAlpha;
-                if (false && (!PixelFormat.formatHasAlpha(mAttrs.format)
+                if (!mLimitedAlphaCompositing
+                        || (!PixelFormat.formatHasAlpha(mAttrs.format)
                         || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
                                 && x == frame.left && y == frame.top))) {
                     //Log.i(TAG, "Applying alpha transform");
@@ -5944,21 +6122,23 @@
             return mHasDrawn;
         }
 
-        public void showLw(boolean doAnimation) {
+        public boolean showLw(boolean doAnimation) {
             if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim) {
-                mSurfacesChanged = true;
                 mPolicyVisibility = true;
                 mPolicyVisibilityAfterAnim = true;
                 if (doAnimation) {
                     applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_ENTER, true);
                 }
                 requestAnimationLocked(0);
+                return true;
             }
+            return false;
         }
 
-        public void hideLw(boolean doAnimation) {
-            if (mPolicyVisibility || mPolicyVisibilityAfterAnim) {
-                mSurfacesChanged = true;
+        public boolean hideLw(boolean doAnimation) {
+            boolean current = doAnimation ? mPolicyVisibilityAfterAnim
+                    : mPolicyVisibility;
+            if (current) {
                 if (doAnimation) {
                     applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
                     if (mAnimation == null) {
@@ -5968,10 +6148,13 @@
                 if (doAnimation) {
                     mPolicyVisibilityAfterAnim = false;
                 } else {
+                    mPolicyVisibilityAfterAnim = false;
                     mPolicyVisibility = false;
                 }
                 requestAnimationLocked(0);
+                return true;
             }
+            return false;
         }
 
         void dump(PrintWriter pw, String prefix) {
@@ -6054,6 +6237,10 @@
         // The type of window this token is for, as per WindowManager.LayoutParams.
         final int windowType;
         
+        // Set if this token was explicitly added by a client, so should
+        // not be removed when all windows are removed.
+        final boolean explicit;
+        
         // If this is an AppWindowToken, this is non-null.
         AppWindowToken appWindowToken;
         
@@ -6069,9 +6256,10 @@
         // Temporary for finding which tokens no longer have visible windows.
         boolean hasVisible;
 
-        WindowToken(IBinder _token, int type) {
+        WindowToken(IBinder _token, int type, boolean _explicit) {
             token = _token;
             windowType = type;
+            explicit = _explicit;
         }
 
         void dump(PrintWriter pw, String prefix) {
@@ -6151,7 +6339,8 @@
         boolean firstWindowDrawn;
 
         AppWindowToken(IApplicationToken _token) {
-            super(_token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION);
+            super(_token.asBinder(),
+                    WindowManager.LayoutParams.TYPE_APPLICATION, true);
             appWindowToken = this;
             appToken = _token;
         }
@@ -6198,17 +6387,10 @@
             for (int i=0; i<N; i++) {
                 WindowState w = allAppWindows.get(i);
                 w.mAnimLayer = w.mLayer + adj;
+                if (DEBUG_LAYERS) Log.v(TAG, "Updating layer " + w + ": "
+                        + w.mAnimLayer);
                 if (w == mInputMethodTarget) {
-                    WindowState imw = mInputMethodWindow;
-                    if (imw != null) {
-                        imw.mAnimLayer = imw.mLayer + adj;
-                    }
-                    int di = mInputMethodDialogs.size();
-                    while (di > 0) {
-                        di --;
-                        imw = mInputMethodDialogs.get(di);
-                        imw.mAnimLayer = imw.mLayer + adj;
-                    }
+                    setInputMethodAnimLayerAdjustment(adj);
                 }
             }
         }
@@ -6295,7 +6477,10 @@
 
             clearAnimation();
             animating = false;
-
+            if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) {
+                moveInputMethodWindowsIfNeededLocked(true);
+            }
+            
             if (DEBUG_ANIM) Log.v(
                     TAG, "Animation done in " + this
                     + ": reportedVisible=" + reportedVisible);
@@ -6519,6 +6704,10 @@
                     synchronized(mWindowMap) {
                         lastFocus = mLastFocus;
                         newFocus = mCurrentFocus;
+                        if (lastFocus == newFocus) {
+                            // Focus is not changing, so nothing to do.
+                            return;
+                        }
                         mLastFocus = newFocus;
                         //Log.i(TAG, "Focus moving from " + lastFocus
                         //        + " to " + newFocus);
@@ -6848,7 +7037,7 @@
         synchronized (mWindowMap) {
             // The focus for the client is the window immediately below
             // where we would place the input method window.
-            int idx = findDesiredInputMethodWindowIndexLocked();
+            int idx = findDesiredInputMethodWindowIndexLocked(false);
             if (idx > 0) {
                 WindowState imFocus = (WindowState)mWindows.get(idx-1);
                 if (imFocus != null && imFocus.mSession.mClient != null &&
@@ -6888,13 +7077,6 @@
     }
 
     private final void assignLayersLocked() {
-        if (mInLayout) {
-            if (Config.DEBUG) {
-                throw new RuntimeException("Recursive call!");
-            }
-            return;
-        }
-
         int N = mWindows.size();
         int curBaseLayer = 0;
         int curLayer = 0;
@@ -6916,6 +7098,11 @@
             } else {
                 w.mAnimLayer = w.mLayer;
             }
+            if (w.mIsImWindow) {
+                w.mAnimLayer += mInputMethodAnimLayerAdjustment;
+            }
+            if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
+                    + w.mAnimLayer);
             //System.out.println(
             //    "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
         }
@@ -6927,6 +7114,7 @@
             if (Config.DEBUG) {
                 throw new RuntimeException("Recursive call!");
             }
+            Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout");
             return;
         }
 
@@ -6999,7 +7187,8 @@
                 WindowState win = (WindowState) mWindows.get(i);
     
                 boolean gone = win.mViewVisibility == View.GONE
-                        || !win.mRelayoutCalled;
+                        || !win.mRelayoutCalled
+                        || win.mToken.hidden;
 
                 // If this view is GONE, then skip it -- keep the current
                 // frame, and let the caller know so they can ignore it
@@ -7285,6 +7474,7 @@
                         // This has changed the visibility of windows, so perform
                         // a new layout to get them all up-to-date.
                         mLayoutNeeded = true;
+                        moveInputMethodWindowsIfNeededLocked(true);
                         performLayoutLockedInner();
                         updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
 
@@ -7297,7 +7487,6 @@
 
             final boolean someoneLosingFocus = mLosingFocus.size() != 0;
 
-            mSurfacesChanged = false;
             boolean obscured = false;
             boolean blurring = false;
             boolean dimming = false;
@@ -7673,7 +7862,10 @@
                 mDimCurrentAlpha += mDimDeltaPerMs
                         * (currentTime-mLastDimAnimTime);
                 boolean more = true;
-                if (mDimDeltaPerMs > 0) {
+                if (mDisplayFrozen) {
+                    // If the display is frozen, there is no reason to animate.
+                    more = false;
+                } else if (mDimDeltaPerMs > 0) {
                     if (mDimCurrentAlpha > mDimTargetAlpha) {
                         more = false;
                     }
@@ -7811,7 +8003,7 @@
     }
     
     /**
-     * Have the surface flinger show a surface, robustly dealing wit
+     * Have the surface flinger show a surface, robustly dealing with
      * error conditions.  In particular, if there is not enough memory
      * to show the surface, then we will try to get rid of other surfaces
      * in order to succeed.
@@ -7925,9 +8117,8 @@
         if (mCurrentFocus != newFocus) {
             // This check makes sure that we don't already have the focus
             // change message pending.
-            if (mLastFocus == mCurrentFocus) {
-                mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
-            }
+            mH.removeMessages(H.REPORT_FOCUS_CHANGE);
+            mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
             if (localLOGV) Log.v(
                 TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
             final WindowState oldFocus = mCurrentFocus;
@@ -8218,13 +8409,14 @@
             pw.println("  mSystemBooted=" + mSystemBooted
                     + " mDisplayEnabled=" + mDisplayEnabled);
             pw.println("  mLayoutNeeded=" + mLayoutNeeded
-                    + " mSurfacesChanged=" + mSurfacesChanged
                     + " mBlurShown=" + mBlurShown);
             pw.println("  mDimShown=" + mDimShown
                     + " current=" + mDimCurrentAlpha
                     + " target=" + mDimTargetAlpha
                     + " delta=" + mDimDeltaPerMs
                     + " lastAnimTime=" + mLastDimAnimTime);
+            pw.println("  mInputMethodAnimLayerAdjustment="
+                    + mInputMethodAnimLayerAdjustment);
             pw.println("  mDisplayFrozen=" + mDisplayFrozen
                     + " mWindowsFreezingScreen=" + mWindowsFreezingScreen
                     + " mAppsFreezingScreen=" + mAppsFreezingScreen);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 361c7f7..f5efe4b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -48,6 +48,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
@@ -96,6 +97,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.PrintWriter;
+import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -631,6 +633,11 @@
      * any user id that can impact battery performance.
      */
     final BatteryStatsService mBatteryStatsService;
+    
+    /**
+     * information about component usage
+     */
+    final UsageStatsService mUsageStatsService;
 
     /**
      * Current configuration information.  HistoryRecord objects are given
@@ -1053,6 +1060,7 @@
         m.mLaunchingActivity.setReferenceCounted(false);
         
         m.mBatteryStatsService.publish(context);
+        m.mUsageStatsService.publish(context);
         
         synchronized (thr) {
             thr.mReady = true;
@@ -1186,8 +1194,7 @@
                     procs = service.mLRUProcesses;
                 }
             }
-            pw.println("Applications Memory Usage (kB):");
-            dumpApplicationMemoryUsage(fd, pw, procs, "  ");
+            dumpApplicationMemoryUsage(fd, pw, procs, "  ", args);
         }
     }
 
@@ -1224,6 +1231,9 @@
                 systemDir, "batterystats.bin").toString());
         mBatteryStatsService.getActiveStatistics().readLocked();
         mBatteryStatsService.getActiveStatistics().writeLocked();
+        
+        mUsageStatsService = new UsageStatsService( new File(
+                systemDir, "usagestats.bin").toString());
 
         mConfiguration.makeDefault();
         mProcessStats.init();
@@ -1522,6 +1532,8 @@
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                     r.info, r.icicle, results, newIntents, !andResume,
                     isNextTransitionForward());
+            // Update usage stats for launched activity
+            updateUsageStats(r, true);
         } catch (RemoteException e) {
             if (r.launchFailed) {
                 // This is the second time we failed -- finish activity
@@ -1804,6 +1816,7 @@
                         prev.shortComponentName);
                 prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
                         prev.configChangeFlags);
+                updateUsageStats(prev, false);
             } catch (Exception e) {
                 // Ignore exception, if process died other code will cleanup.
                 Log.w(TAG, "Exception thrown during pause", e);
@@ -1848,6 +1861,7 @@
     private final void completePauseLocked() {
         HistoryRecord prev = mPausingActivity;
         if (DEBUG_PAUSE) Log.v(TAG, "Complete pause: " + prev);
+        
         if (prev != null) {
             if (prev.finishing) {
                 if (DEBUG_PAUSE) Log.v(TAG, "Executing finish of activity: " + prev);
@@ -2085,6 +2099,14 @@
             ensureActivitiesVisibleLocked(r, starting, null, configChanges);
         }
     }
+    
+    private void updateUsageStats(HistoryRecord resumedComponent, boolean resumed) {
+        if (resumed) {
+            mUsageStatsService.noteResumeComponent(resumedComponent.realActivity);
+        } else {
+            mUsageStatsService.notePauseComponent(resumedComponent.realActivity);
+        }
+    }
 
     /**
      * Ensure that the top activity in the stack is resumed.
@@ -2290,6 +2312,7 @@
                 EventLog.writeEvent(LOG_AM_RESUME_ACTIVITY,
                         System.identityHashCode(next),
                         next.task.taskId, next.shortComponentName);
+                updateUsageStats(next, true);
                 
                 next.app.thread.scheduleResumeActivity(next,
                         isNextTransitionForward());
@@ -2453,9 +2476,7 @@
                     // We don't want to reuse the previous starting preview if:
                     // (1) The current activity is in a different task.
                     if (prev.task != r.task) prev = null;
-                    // (2) The current activity is not the first in the task.
-                    else if (!prev.frontOfTask) prev = null;
-                    // (3) The current activity is already displayed.
+                    // (2) The current activity is already displayed.
                     else if (prev.nowVisible) prev = null;
                 }
                 mWindowManager.setAppStartingWindow(
@@ -2537,6 +2558,40 @@
     }
 
     /**
+     * Find the activity in the history stack within the given task.  Returns
+     * the index within the history at which it's found, or < 0 if not found.
+     */
+    private final int findActivityInHistoryLocked(HistoryRecord r, int task) {
+        int i = mHistory.size();
+        while (i > 0) {
+            i--;
+            HistoryRecord candidate = (HistoryRecord)mHistory.get(i);
+            if (candidate.task.taskId != task) {
+                break;
+            }
+            if (candidate.realActivity.equals(r.realActivity)) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Reorder the history stack so that the activity at the given index is
+     * brought to the front.
+     */
+    private final HistoryRecord moveActivityToFrontLocked(int where) {
+        HistoryRecord newTop = (HistoryRecord)mHistory.remove(where);
+        int top = mHistory.size();
+        HistoryRecord oldTop = (HistoryRecord)mHistory.get(top-1);
+        mHistory.add(top, newTop);
+        oldTop.frontOfTask = false;
+        newTop.frontOfTask = true;
+        return newTop;
+    }
+
+    /**
      * Deliver a new Intent to an existing activity, so that its onNewIntent()
      * method will be called at the proper time.
      */
@@ -2969,6 +3024,19 @@
                     resumeTopActivityLocked(null);
                     return START_DELIVERED_TO_TOP;
                 }
+            } else if (!addingToTask &&
+                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+                // In this case, we are launching an activity in our own task
+                // that may already be running somewhere in the history, and
+                // we want to shuffle it to the front of the stack if so.
+                int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId);
+                if (where >= 0) {
+                    HistoryRecord top = moveActivityToFrontLocked(where);
+                    logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
+                    deliverNewIntentLocked(top, r.intent);
+                    resumeTopActivityLocked(null);
+                    return START_DELIVERED_TO_TOP;
+                }
             }
             // An existing activity is starting this new activity, so we want
             // to keep the new one in the same task as the one that is starting
@@ -4142,8 +4210,8 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = ActivityThread.getPackageManager();
+            int pkgUid = -1;
             synchronized(this) {
-                int pkgUid = -1;
                 try {
                     pkgUid = pm.getPackageUid(packageName);
                 } catch (RemoteException e) {
@@ -4156,7 +4224,7 @@
                         android.Manifest.permission.CLEAR_APP_USER_DATA,
                         pid, uid, -1)
                         == PackageManager.PERMISSION_GRANTED) {
-                    uninstallPackageLocked(packageName, pkgUid, false);
+                    restartPackageLocked(packageName, pkgUid);
                 } else {
                     throw new SecurityException(pid+" does not have permission:"+
                             android.Manifest.permission.CLEAR_APP_USER_DATA+" to clear data" +
@@ -4167,6 +4235,12 @@
             try {
                 //clear application user data
                 pm.clearApplicationUserData(packageName, observer);
+                Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
+                        Uri.fromParts("package", packageName, null));
+                intent.putExtra(Intent.EXTRA_UID, pkgUid);
+                broadcastIntentLocked(null, null, intent,
+                        null, null, 0, null, null, null,
+                        false, false, MY_PID, Process.SYSTEM_UID);
             } catch (RemoteException e) {
             }
         } finally {
@@ -4188,25 +4262,48 @@
         
         long callingId = Binder.clearCallingIdentity();
         try {
+            IPackageManager pm = ActivityThread.getPackageManager();
+            int pkgUid = -1;
             synchronized(this) {
-                uninstallPackageLocked(packageName, -1, false);
-                broadcastIntentLocked(null, null,
-                        new Intent(Intent.ACTION_PACKAGE_RESTARTED,
-                                Uri.fromParts("package", packageName, null)),
-                        null, null, 0, null, null, null,
-                        false, false, MY_PID, Process.SYSTEM_UID);
+                try {
+                    pkgUid = pm.getPackageUid(packageName);
+                } catch (RemoteException e) {
+                }
+                if (pkgUid == -1) {
+                    Log.w(TAG, "Invalid packageName:"+packageName);
+                    return;
+                }
+                restartPackageLocked(packageName, pkgUid);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
     }
     
+    private void restartPackageLocked(final String packageName, int uid) {
+        uninstallPackageLocked(packageName, uid, false);
+        Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
+                Uri.fromParts("package", packageName, null));
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        broadcastIntentLocked(null, null, intent,
+                null, null, 0, null, null, null,
+                false, false, MY_PID, Process.SYSTEM_UID);
+    }
+    
     private final void uninstallPackageLocked(String name, int uid,
             boolean callerWillRestart) {
         if (Config.LOGD) Log.d(TAG, "Uninstalling process " + name);
 
         int i, N;
 
+        final String procNamePrefix = name + ":";
+        if (uid < 0) {
+            try {
+                uid = ActivityThread.getPackageManager().getPackageUid(name);
+            } catch (RemoteException e) {
+            }
+        }
+
         Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
         while (badApps.hasNext()) {
             SparseArray<Long> ba = badApps.next();
@@ -4217,14 +4314,6 @@
 
         ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
 
-        final String procNamePrefix = name + ":";
-        if (uid < 0) {
-            try {
-                uid = ActivityThread.getPackageManager().getPackageUid(name);
-            } catch (RemoteException e) {
-            }
-        }
-
         // Remove all processes this package may have touched: all with the
         // same UID (except for the system or root user), and all whose name
         // matches the package name.
@@ -7226,6 +7315,11 @@
         }
     }
 
+    public boolean testIsSystemReady() {
+        // no need to synchronize(this) just to read & return the value
+        return mSystemReady;
+    }
+    
     public void systemReady() {
         // In the simulator, startRunning will never have been called, which
         // normally sets a few crucial variables. Do it here instead.
@@ -7628,6 +7722,21 @@
                     ActivityManager.RunningAppProcessInfo currApp = 
                         new ActivityManager.RunningAppProcessInfo(app.processName,
                                 app.pid, app.getPackageList());
+                    int adj = app.curAdj;
+                    if (adj >= CONTENT_PROVIDER_ADJ) {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
+                    } else if (adj >= HIDDEN_APP_MIN_ADJ) {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+                        currApp.lru = adj - HIDDEN_APP_MIN_ADJ;
+                    } else if (adj >= SECONDARY_SERVER_ADJ) {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+                    } else if (adj >= VISIBLE_APP_ADJ) {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+                    } else {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+                    }
+                    //Log.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
+                    //        + " lru=" + currApp.lru);
                     if (runList == null) {
                         runList = new ArrayList<ActivityManager.RunningAppProcessInfo>();
                     }
@@ -8176,30 +8285,55 @@
     }
 
     private static final void dumpApplicationMemoryUsage(FileDescriptor fd,
-            PrintWriter pw, List list, String prefix) {
+            PrintWriter pw, List list, String prefix, String[] args) {
+        final boolean isCheckinRequest = scanArgs(args, "-c");
+        long uptime = SystemClock.uptimeMillis();
+        long realtime = SystemClock.elapsedRealtime();
+        
+        if (isCheckinRequest) {
+            // short checkin version
+            pw.println(uptime + "," + realtime);
+            pw.flush();
+        } else {
+            pw.println("Applications Memory Usage (kB):");
+            pw.println("Uptime: " + uptime + " Realtime: " + realtime);
+        }
         for (int i = list.size() - 1 ; i >= 0 ; i--) {
             ProcessRecord r = (ProcessRecord)list.get(i);
             if (r.thread != null) {
-                pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
-                pw.flush();
-
-                Parcel data = Parcel.obtain();
-                Parcel reply = Parcel.obtain();
-                try {
-                    data.writeFileDescriptor(fd);
-                    r.thread.asBinder().transact(IBinder.DUMP_TRANSACTION, data, reply, 0);
-
-                } catch (RemoteException e) {
-                    pw.println("Got RemoteException!");
+                if (!isCheckinRequest) {
+                    pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
                     pw.flush();
                 }
-
-                data.recycle();
-                reply.recycle();
+                try {
+                    r.thread.asBinder().dump(fd, args);
+                } catch (RemoteException e) {
+                    if (!isCheckinRequest) {
+                        pw.println("Got RemoteException!");
+                        pw.flush();
+                    }
+                }
             }
         }
     }
 
+    /**
+     * 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;
+    }
+
     private final int indexOfTokenLocked(IBinder token, boolean required) {
         int count = mHistory.size();
 
@@ -9120,6 +9254,36 @@
         return 0;
     }
 
+    public IBinder peekService(Intent service, String resolvedType) {
+        // Refuse possible leaked file descriptors
+        if (service != null && service.hasFileDescriptors() == true) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        IBinder ret = null;
+
+        synchronized(this) {
+            ServiceLookupResult r = findServiceLocked(service, resolvedType);
+            
+            if (r != null) {
+                // r.record is null if findServiceLocked() failed the caller permission check
+                if (r.record == null) {
+                    throw new SecurityException(
+                            "Permission Denial: Accessing service " + r.record.name
+                            + " from pid=" + Binder.getCallingPid()
+                            + ", uid=" + Binder.getCallingUid()
+                            + " requires " + r.permission);
+                }
+                IntentBindRecord ib = r.record.bindings.get(r.record.intent);
+                if (ib != null) {
+                    ret = ib.binder;
+                }
+            }
+        }
+
+        return ret;
+    }
+    
     public boolean stopServiceToken(ComponentName className, IBinder token,
             int startId) {
         synchronized(this) {
@@ -9928,6 +10092,20 @@
         }
 
         synchronized(this) {
+            if (!mSystemReady) {
+                // if the caller really truly claims to know what they're doing, go
+                // ahead and allow the broadcast without launching any receivers
+                int flags = intent.getFlags();
+                if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
+                    intent = new Intent(intent);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0){
+                    Log.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
+                            + " before boot completion");
+                    throw new IllegalStateException("Cannot broadcast before boot completed");
+                }
+            }
+            
             final ProcessRecord callerApp = getRecordForAppLocked(caller);
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
@@ -10639,6 +10817,22 @@
     // =========================================================
     // CONFIGURATION
     // =========================================================
+    
+    public ConfigurationInfo getDeviceConfigurationInfo() {
+        ConfigurationInfo config = new ConfigurationInfo();
+        synchronized (this) {
+            config.reqTouchScreen = mConfiguration.touchscreen;
+            config.reqKeyboardType = mConfiguration.keyboard;
+            config.reqNavigation = mConfiguration.navigation;
+            if (mConfiguration.navigation != Configuration.NAVIGATION_NONAV) {
+                config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+            }
+            if (mConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED) {
+                config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+            }
+        }
+        return config;
+    }
 
     public Configuration getConfiguration() {
         Configuration ci;
@@ -10653,6 +10847,11 @@
                 "updateConfiguration()");
 
         synchronized(this) {
+            if (values == null && mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mWindowManager.computeNewConfiguration();
+            }
+            
             final long origId = Binder.clearCallingIdentity();
             updateConfigurationLocked(values, null);
             Binder.restoreCallingIdentity(origId);
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index bf1bc8c..1cd6298 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -65,6 +65,8 @@
     }
     
     public BatteryStatsImpl getStatistics() {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.BATTERY_STATS, null);
         return mStats;
     }
     
@@ -82,17 +84,59 @@
         }
     }
 
-    public void noteStartSensor(int uid, int sensor) {
+    public void noteStartSensor(int uid, String name, int sensor) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+            mStats.getUidStatsLocked(uid).noteStartSensor(name, sensor);
         }
     }
     
-    public void noteStopSensor(int uid, int sensor) {
+    public void noteStopSensor(int uid, String name, int sensor) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+            mStats.getUidStatsLocked(uid).noteStopSensor(name, sensor);
+        }
+    }
+    
+    public void noteStartGps(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteStartGps(uid);
+        }
+    }
+    
+    public void noteStopGps(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteStopGps(uid);
+        }
+    }
+    
+    public void noteRequestGpsOn(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteRequestGpsOn(uid);
+        }
+    }
+    
+    public void noteRequestGpsOff(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteRequestGpsOff(uid);
+        }
+    }
+    
+    public void noteScreenOn() {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteScreenOn();
+        }
+    }
+    
+    public void noteScreenOff() {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteScreenOff();
         }
     }
 
@@ -106,10 +150,14 @@
     }
     
     public long getAwakeTimeBattery() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BATTERY_STATS, null);
         return mStats.getAwakeTimeBattery();
     }
 
     public long getAwakeTimePlugged() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BATTERY_STATS, null);
         return mStats.getAwakeTimePlugged();
     }
 
@@ -117,7 +165,7 @@
         if (Binder.getCallingPid() == Process.myPid()) {
             return;
         }
-        mContext.enforcePermission(android.Manifest.permission.BATTERY_STATS,
+        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
     
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index b370b1c..b407208 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -23,6 +23,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.os.Bundle;
@@ -184,6 +185,11 @@
             dataDir = aInfo.applicationInfo.dataDir;
             nonLocalizedLabel = aInfo.nonLocalizedLabel;
             labelRes = aInfo.labelRes;
+            if (nonLocalizedLabel == null && labelRes == 0) {
+                ApplicationInfo app = aInfo.applicationInfo;
+                nonLocalizedLabel = app.nonLocalizedLabel;
+                labelRes = app.labelRes;
+            }
             icon = aInfo.getIconResource();
             theme = aInfo.getThemeResource();
             if ((aInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
new file mode 100755
index 0000000..001987f
--- /dev/null
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2006-2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.app.IUsageStats;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import com.android.internal.os.PkgUsageStats;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This service collects the statistics associated with usage
+ * of various components, like when a particular package is launched or
+ * paused and aggregates events like number of time a component is launched
+ * total duration of a component launch.
+ */
+public final class UsageStatsService extends IUsageStats.Stub {
+    public static final String SERVICE_NAME = "usagestats";
+    private static final boolean localLOGV = false;
+    private static final String TAG = "UsageStats";
+    static IUsageStats sService;
+    private Context mContext;
+    private String mFileName;
+    final private Map<String, PkgUsageStatsExtended> mStats;
+    private String mResumedPkg;
+    
+    private class PkgUsageStatsExtended {
+        int mLaunchCount;
+        long mUsageTime;
+        long mChgTime;
+        PkgUsageStatsExtended() {
+            mLaunchCount = 0;
+            mUsageTime = 0;
+            mChgTime =  SystemClock.elapsedRealtime();
+        }
+        void updateResume() {
+            mLaunchCount ++;
+            mChgTime = SystemClock.elapsedRealtime();
+        }
+        void updatePause() {
+            long currTime = SystemClock.elapsedRealtime();
+            mUsageTime += (currTime - mChgTime);
+            mChgTime = currTime;
+        }
+    }
+    
+    UsageStatsService(String filename) {
+        mFileName = filename;
+        mStats = new HashMap<String, PkgUsageStatsExtended>();
+    }
+    
+    public void publish(Context context) {
+        mContext = context;
+        ServiceManager.addService(SERVICE_NAME, asBinder());
+    }
+    
+    public static IUsageStats getService() {
+        if (sService != null) {
+            return sService;
+        }
+        IBinder b = ServiceManager.getService(SERVICE_NAME);
+        sService = asInterface(b);
+        return sService;
+    }
+    
+    public void noteResumeComponent(ComponentName componentName) {
+        enforceCallingPermission();
+        String pkgName;
+        if ((componentName == null) ||
+                ((pkgName = componentName.getPackageName()) == null)) {
+            return;
+        }
+        if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) {
+            // Moving across activities in same package. just return
+            return;
+        } 
+        if (localLOGV) Log.i(TAG, "started component:"+pkgName);
+        PkgUsageStatsExtended pus = mStats.get(pkgName);
+        if (pus == null) {
+            pus = new PkgUsageStatsExtended();
+            mStats.put(pkgName, pus);
+        }
+        pus.updateResume();
+        mResumedPkg = pkgName;
+    }
+
+    public void notePauseComponent(ComponentName componentName) {
+        enforceCallingPermission();
+        String pkgName;
+        if ((componentName == null) ||
+                ((pkgName = componentName.getPackageName()) == null)) {
+            return;
+        }
+        if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) {
+            Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused");
+            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;
+        }
+        pus.updatePause();
+    }
+    
+    public void enforceCallingPermission() {
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return;
+        }
+        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), null);
+    }
+    
+    public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
+        String pkgName;
+        if ((componentName == null) ||
+                ((pkgName = componentName.getPackageName()) == null)) {
+            return null;
+        }
+        PkgUsageStatsExtended pus = mStats.get(pkgName);
+        if (pus == null) {
+            return null;
+        }
+        return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime);
+    }
+    
+    public PkgUsageStats[] getAllPkgUsageStats() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
+        synchronized (mStats) {
+            Set<String> keys = mStats.keySet();
+            int size = keys.size();
+            if (size <= 0) {
+                return null;
+            }
+            PkgUsageStats retArr[] = new PkgUsageStats[size];
+            int i = 0;
+            for (String key: keys) {
+                PkgUsageStatsExtended pus = mStats.get(key);
+                retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime);
+                i++;
+            }
+            return retArr;
+        }
+    }
+    
+    @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");
+            }
+        }
+        pw.write(sb.toString());
+    }
+}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 00ff7be..3a5b13c 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -157,6 +157,7 @@
     private IconData mBluetoothData;
     private int mBluetoothHeadsetState;
     private int mBluetoothA2dpState;
+    private boolean mBluetoothEnabled;
 
     // wifi
     private static final int[] sWifiSignalImages = new int[] {
@@ -286,7 +287,16 @@
         mBluetoothData = IconData.makeIcon("bluetooth",
                 null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
         mBluetoothIcon = service.addIcon(mBluetoothData, null);
-        updateBluetooth(null);
+        BluetoothDevice bluetooth =
+                (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+        if (bluetooth != null) {
+            mBluetoothEnabled = bluetooth.isEnabled();
+        } else {
+            mBluetoothEnabled = false;
+        }
+        mBluetoothA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
+        mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
+        mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
 
         // Gps status
         mGpsEnabledIconData = IconData.makeIcon("gps",
@@ -767,30 +777,17 @@
     }
 
     private final void updateBluetooth(Intent intent) {
-        boolean visible = false;
-        if (intent == null) {  // Initialize
-            BluetoothDevice bluetooth =
-                (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
-            if (bluetooth != null) {
-                visible = bluetooth.isEnabled();
-            }
-            mService.setIconVisibility(mBluetoothIcon, visible);
-            return;
-        }
-
         int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
-        String action = intent.getAction();
 
+        String action = intent.getAction();
         if (action.equals(BluetoothIntent.DISABLED_ACTION)) {
-            visible = false;
+            mBluetoothEnabled = false;
         } else if (action.equals(BluetoothIntent.ENABLED_ACTION)) {
-            visible = true;
+            mBluetoothEnabled = true;
         } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
-            visible = true;
             mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
                     BluetoothHeadset.STATE_ERROR);
         } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
-            visible = true;
             mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
                     BluetoothA2dp.STATE_DISCONNECTED);
         } else {
@@ -805,7 +802,7 @@
 
         mBluetoothData.iconId = iconId;
         mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
-        mService.setIconVisibility(mBluetoothIcon, visible);
+        mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
     }
 
     private final void updateWifi(Intent intent) {
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 3b6e354..a4844b1 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -322,9 +322,11 @@
     }
 
     public void systemReady() {
+        final StatusBarView view = mStatusBarView;
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.FILL_PARENT,
-                25,
+                view.getContext().getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height),
                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
@@ -333,7 +335,7 @@
         lp.setTitle("StatusBar");
         lp.windowAnimations = R.style.Animation_StatusBar;
 
-        WindowManagerImpl.getDefault().addView(mStatusBarView, lp);
+        WindowManagerImpl.getDefault().addView(view, lp);
     }
     
     // ================================================================================