donut snapshot
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index d66c6e5..9c4ace1 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -413,41 +413,45 @@
         
         synchronized (mLock) {
             pw.println("Current Alarm Manager state:");
-            if (mRtcWakeupAlarms.size() > 0) {
+            if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
                 pw.println(" ");
-                pw.println("  Realtime wakeup alarms that are scheduled:");
-                dumpAlarmList(pw, mRtcWakeupAlarms, "  ", "RTC_WAKEUP");
+                pw.print("  Realtime wakeup (now=");
+                        pw.print(System.currentTimeMillis()); pw.println("):");
+                if (mRtcWakeupAlarms.size() > 0) {
+                    dumpAlarmList(pw, mRtcWakeupAlarms, "  ", "RTC_WAKEUP");
+                }
+                if (mRtcAlarms.size() > 0) {
+                    dumpAlarmList(pw, mRtcAlarms, "  ", "RTC");
+                }
             }
-            if (mRtcAlarms.size() > 0) {
+            if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
                 pw.println(" ");
-                pw.println("  Realtime alarms that are scheduled:");
-                dumpAlarmList(pw, mRtcAlarms, "  ", "RTC");
-            }
-            if (mElapsedRealtimeWakeupAlarms.size() > 0) {
-                pw.println(" ");
-                pw.println("  Elapsed realtime wakeup alarms that are scheduled:");
-                dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, "  ", "ELAPSED_REALTIME_WAKEUP");
-            }
-            if (mElapsedRealtimeAlarms.size() > 0) {
-                pw.println(" ");
-                pw.println("  Elapsed realtime alarms that are scheduled:");
-                dumpAlarmList(pw, mElapsedRealtimeAlarms, "  ", "ELAPSED_REALTIME");
+                pw.print("  Elapsed realtime wakeup (now=");
+                        pw.print(SystemClock.elapsedRealtime()); pw.println("):");
+                if (mElapsedRealtimeWakeupAlarms.size() > 0) {
+                    dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, "  ", "ELAPSED_WAKEUP");
+                }
+                if (mElapsedRealtimeAlarms.size() > 0) {
+                    dumpAlarmList(pw, mElapsedRealtimeAlarms, "  ", "ELAPSED");
+                }
             }
             
             pw.println(" ");
-            pw.println("  Broadcast ref count: " + mBroadcastRefCount);
+            pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
             
             pw.println(" ");
             pw.println("  Alarm Stats:");
             for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
                 BroadcastStats bs = be.getValue();
-                pw.println("  " + be.getKey());
-                pw.println("    " + bs.aggregateTime + "ms running, "
-                        + bs.numWakeup + " wakeups");
+                pw.print("  "); pw.println(be.getKey());
+                pw.print("    "); pw.print(bs.aggregateTime);
+                        pw.print("ms running, "); pw.print(bs.numWakeup);
+                        pw.println(" wakeups");
                 for (Map.Entry<Intent.FilterComparison, FilterStats> fe
                         : bs.filterStats.entrySet()) {
-                    pw.println("    " + fe.getValue().count + " alarms: "
-                            + fe.getKey().getIntent());
+                    pw.print("    "); pw.print(fe.getValue().count);
+                            pw.print(" alarms: ");
+                            pw.println(fe.getKey().getIntent().toShortString(true, false));
                 }
             }
         }
@@ -456,7 +460,8 @@
     private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label) {
         for (int i=list.size()-1; i>=0; i--) {
             Alarm a = list.get(i);
-            pw.println(prefix + label + " #" + i + ":");
+            pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
+                    pw.print(": "); pw.println(a);
             a.dump(pw, prefix + "  ");
         }
     }
@@ -561,18 +566,24 @@
         @Override
         public String toString()
         {
-            return "Alarm{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " type " + type + " " + operation.getTargetPackage() + "}";
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("Alarm{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" type ");
+            sb.append(type);
+            sb.append(" ");
+            sb.append(operation.getTargetPackage());
+            sb.append('}');
+            return sb.toString();
         }
 
         public void dump(PrintWriter pw, String prefix)
         {
-            pw.println(prefix + this);
-            pw.println(prefix + "type=" + type + " when=" + when
-                  + " repeatInterval=" + repeatInterval
-                  + " count=" + count);
-            pw.println(prefix + "operation=" + operation);
+            pw.print(prefix); pw.print("type="); pw.print(type);
+                    pw.print(" when="); pw.print(when);
+                    pw.print(" repeatInterval="); pw.print(repeatInterval);
+                    pw.print(" count="); pw.println(count);
+            pw.print(prefix); pw.print("operation="); pw.println(operation);
         }
     }
     
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index de5d0ac..c50ae94 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -35,6 +35,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -151,34 +152,57 @@
 
         synchronized (mAppWidgetIds) {
             int N = mInstalledProviders.size();
-            pw.println("Providers: (size=" + N + ")");
+            pw.println("Providers:");
             for (int i=0; i<N; i++) {
                 Provider p = mInstalledProviders.get(i);
                 AppWidgetProviderInfo info = p.info;
-                pw.println("  [" + i + "] provder=" + info.provider
-                        + " min=(" + info.minWidth + "x" + info.minHeight + ")"
-                        + " updatePeriodMillis=" + info.updatePeriodMillis
-                        + " initialLayout=" + info.initialLayout + " zombie=" + p.zombie);
+                pw.print("  ["); pw.print(i); pw.print("] provider ");
+                        pw.print(info.provider.flattenToShortString());
+                        pw.println(':');
+                pw.print("    min=("); pw.print(info.minWidth);
+                        pw.print("x"); pw.print(info.minHeight);
+                        pw.print(") updatePeriodMillis=");
+                        pw.print(info.updatePeriodMillis);
+                        pw.print(" initialLayout=#");
+                        pw.print(Integer.toHexString(info.initialLayout));
+                        pw.print(" zombie="); pw.println(p.zombie);
             }
 
             N = mAppWidgetIds.size();
-            pw.println("AppWidgetIds: (size=" + N + ")");
+            pw.println(" ");
+            pw.println("AppWidgetIds:");
             for (int i=0; i<N; i++) {
                 AppWidgetId id = mAppWidgetIds.get(i);
-                pw.println("  [" + i + "] appWidgetId=" + id.appWidgetId
-                        + " 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);
+                pw.print("  ["); pw.print(i); pw.print("] id=");
+                        pw.println(id.appWidgetId);;
+                pw.print("    hostId=");
+                        pw.print(id.host.hostId); pw.print(' ');
+                        pw.print(id.host.packageName); pw.print('/');
+                        pw.println(id.host.uid);
+                if (id.provider != null) {
+                    pw.print("    provider=");
+                            pw.println(id.provider.info.provider.flattenToShortString());
+                }
+                if (id.host != null) {
+                    pw.print("    host.callbacks="); pw.println(id.host.callbacks);
+                }
+                if (id.views != null) {
+                    pw.print("    views="); pw.println(id.views);
+                }
             }
 
             N = mHosts.size();
-            pw.println("Hosts: (size=" + N + ")");
+            pw.println(" ");
+            pw.println("Hosts:");
             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() + " zombie=" + host.zombie);
+                pw.print("  ["); pw.print(i); pw.print("] hostId=");
+                        pw.print(host.hostId); pw.print(' ');
+                        pw.print(host.packageName); pw.print('/');
+                        pw.print(host.uid); pw.println(':');
+                pw.print("    callbacks="); pw.println(host.callbacks);
+                pw.print("    instances.size="); pw.print(host.instances.size());
+                        pw.print(" zombie="); pw.println(host.zombie);
             }
         }
     }
@@ -716,7 +740,7 @@
             throw new IllegalArgumentException("packageName and uid don't match packageName="
                     + packageName);
         }
-        if (callingUid != packageUid) {
+        if (callingUid != packageUid && Process.supportsProcesses()) {
             throw new IllegalArgumentException("packageName and uid don't match packageName="
                     + packageName);
         }
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
new file mode 100644
index 0000000..983329b
--- /dev/null
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2009 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.backup.BackupService;
+import android.backup.IBackupService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import android.backup.IBackupManager;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+class BackupManagerService extends IBackupManager.Stub {
+    private static final String TAG = "BackupManagerService";
+    private static final boolean DEBUG = true;
+    
+    private static final long COLLECTION_INTERVAL = 1000;
+    //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+
+    private static final int MSG_RUN_BACKUP = 1;
+    
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private final BackupHandler mBackupHandler = new BackupHandler();
+    // map UIDs to the set of backup client services within that UID's app set
+    private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
+        = new SparseArray<HashSet<ServiceInfo>>();
+    // set of backup services that have pending changes
+    private class BackupRequest {
+        public ServiceInfo service;
+        public boolean fullBackup;
+        
+        BackupRequest(ServiceInfo svc, boolean isFull) {
+            service = svc;
+            fullBackup = isFull;
+        }
+    }
+    // Backups that we haven't started yet.
+    private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap();
+    // Backups that we have started.  These are separate to prevent starvation
+    // if an app keeps re-enqueuing itself.
+    private ArrayList<BackupRequest> mBackupQueue;
+    private final Object mQueueLock = new Object();
+
+    private File mStateDir;
+    private File mDataDir;
+    
+    public BackupManagerService(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+
+        // Set up our bookkeeping
+        mStateDir = new File(Environment.getDataDirectory(), "backup");
+        mStateDir.mkdirs();
+        mDataDir = Environment.getDownloadCacheDirectory();
+        
+        // Build our mapping of uid to backup client services
+        synchronized (mBackupParticipants) {
+            addPackageParticipantsLocked(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_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(mBroadcastReceiver, filter);
+    }
+
+    // ----- Track installation/removal of packages -----
+    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Log.d(TAG, "Received broadcast " + intent);
+
+            Uri uri = intent.getData();
+            if (uri == null) {
+                return;
+            }
+            String pkgName = uri.getSchemeSpecificPart();
+            if (pkgName == null) {
+                return;
+            }
+
+            String action = intent.getAction();
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                synchronized (mBackupParticipants) {
+                    Bundle extras = intent.getExtras();
+                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                        // The package was just upgraded
+                        updatePackageParticipantsLocked(pkgName);
+                    } else {
+                        // The package was just added
+                        addPackageParticipantsLocked(pkgName);
+                    }
+                }
+            }
+            else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                Bundle extras = intent.getExtras();
+                if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
+                } else {
+                    synchronized (mBackupParticipants) {
+                        removePackageParticipantsLocked(pkgName);
+                    }
+                }
+            }
+        }
+    };
+
+    // ----- Run the actual backup process asynchronously -----
+
+    private class BackupHandler extends Handler implements ServiceConnection {
+        public void handleMessage(Message msg) {
+
+            switch (msg.what) {
+            case MSG_RUN_BACKUP:
+                // snapshot the pending-backup set and work on that
+                synchronized (mQueueLock) {
+                    mBackupQueue = new ArrayList();
+                    for (BackupRequest b: mPendingBackups.values()) {
+                        mBackupQueue.add(b);
+                    }
+                    mPendingBackups = new HashMap<ComponentName,BackupRequest>();
+                    // !!! TODO: start a new backup-queue journal file too
+                    // WARNING: If we crash after this line, anything in mPendingBackups will
+                    // be lost.  FIX THIS.
+                }
+                startOneService();
+                break;
+            }
+        }
+        
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "onServiceConnected name=" + name + " service=" + service);
+            IBackupService bs = IBackupService.Stub.asInterface(service);
+            processOneBackup(name, bs);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            // TODO: handle backup being interrupted
+        }
+    }
+
+    void startOneService() {
+        // Loop until we find someone to start or the queue empties out.
+        Intent intent = new Intent(BackupService.SERVICE_ACTION);
+        while (true) {
+            BackupRequest request;
+            synchronized (mQueueLock) {
+                int queueSize = mBackupQueue.size();
+                if (queueSize == 0) {
+                    mBackupQueue = null;
+                    // TODO: Anything else to do here?
+                    return;
+                }
+                request = mBackupQueue.get(0);
+                // Take it off the queue when we're done.
+            }
+            
+            intent.setClassName(request.service.packageName, request.service.name);
+            Log.d(TAG, "binding to " + intent);
+            try {
+                if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) {
+                    Log.d(TAG, "awaiting service object for " + intent);
+                    // success
+                    return;
+                }
+            } catch (SecurityException ex) {
+                // Try for the next one.
+                Log.d(TAG, "error in bind", ex);
+            }
+        }
+    }
+
+    void processOneBackup(ComponentName name, IBackupService bs) {
+        try {
+            Log.d(TAG, "processOneBackup doBackup() on " + name);
+
+            BackupRequest request;
+            synchronized (mQueueLock) {
+                if (mBackupQueue == null) {
+                    Log.d(TAG, "mBackupQueue is null.  WHY?");
+                }
+                request = mBackupQueue.get(0);
+            }
+
+            // !!! TODO right now these naming schemes limit applications to
+            // one backup service per package
+            File savedStateName = new File(mStateDir, request.service.packageName);
+            File backupDataName = new File(mDataDir, request.service.packageName + ".data");
+            File newStateName = new File(mStateDir, request.service.packageName + ".new");
+            
+            // In a full backup, we pass a null ParcelFileDescriptor as
+            // the saved-state "file"
+            ParcelFileDescriptor savedState = (request.fullBackup) ? null
+                    : ParcelFileDescriptor.open(savedStateName,
+                        ParcelFileDescriptor.MODE_READ_ONLY |
+                        ParcelFileDescriptor.MODE_CREATE);
+
+            backupDataName.delete();
+            ParcelFileDescriptor backupData =
+                    ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+            newStateName.delete();
+            ParcelFileDescriptor newState =
+                    ParcelFileDescriptor.open(newStateName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+            // Run the target's backup pass
+            try {
+                // TODO: Make this oneway
+                bs.doBackup(savedState, backupData, newState);
+            } finally {
+                if (savedState != null) {
+                    savedState.close();
+                }
+                backupData.close();
+                newState.close();
+            }
+
+            // !!! TODO: Now propagate the newly-backed-up data to the transport
+            
+            // !!! TODO: After successful transport, delete the now-stale data
+            // and juggle the files so that next time the new state is passed
+            backupDataName.delete();
+            newStateName.renameTo(savedStateName);
+            
+        } catch (FileNotFoundException fnf) {
+            Log.d(TAG, "File not found on backup: ");
+            fnf.printStackTrace();
+        } catch (RemoteException e) {
+            Log.d(TAG, "Remote target " + name + " threw during backup:");
+            e.printStackTrace();
+        } catch (Exception e) {
+            Log.w(TAG, "Final exception guard in backup: ");
+            e.printStackTrace();
+        }
+        synchronized (mQueueLock) {
+            mBackupQueue.remove(0);
+        }
+        mContext.unbindService(mBackupHandler);
+    }
+
+    // Add the backup services in the given package to our set of known backup participants.
+    // If 'packageName' is null, adds all backup services in the system.
+    void addPackageParticipantsLocked(String packageName) {
+        List<ResolveInfo> services = mPackageManager.queryIntentServices(
+                new Intent(BackupService.SERVICE_ACTION), 0);
+        addPackageParticipantsLockedInner(packageName, services);
+    }
+
+    private void addPackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) {
+        for (ResolveInfo ri : services) {
+            if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
+                int uid = ri.serviceInfo.applicationInfo.uid;
+                HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
+                if (set == null) {
+                    set = new HashSet<ServiceInfo>();
+                    mBackupParticipants.put(uid, set);
+                }
+                if (DEBUG) {
+                    Log.v(TAG, "Adding " + services.size() + " backup participants:");
+                    for (ResolveInfo svc : services) {
+                        Log.v(TAG, "    " + svc + " : " + svc.filter);
+                    }
+                }
+
+                set.add(ri.serviceInfo);
+            }
+        }
+    }
+
+    // Remove the given package's backup services from our known active set.  If
+    // 'packageName' is null, *all* backup services will be removed.
+    void removePackageParticipantsLocked(String packageName) {
+        List<ResolveInfo> services = mPackageManager.queryIntentServices(
+                new Intent(BackupService.SERVICE_ACTION), 0);
+        removePackageParticipantsLockedInner(packageName, services);
+    }
+
+    private void removePackageParticipantsLockedInner(String packageName,
+            List<ResolveInfo> services) {
+        for (ResolveInfo ri : services) {
+            if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
+                int uid = ri.serviceInfo.applicationInfo.uid;
+                HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
+                if (set != null) {
+                    set.remove(ri.serviceInfo);
+                    if (set.size() == 0) {
+                        mBackupParticipants.put(uid, null);
+                    }
+                }
+            }
+        }
+    }
+
+    // Reset the given package's known backup participants.  Unlike add/remove, the update
+    // action cannot be passed a null package name.
+    void updatePackageParticipantsLocked(String packageName) {
+        if (packageName == null) {
+            Log.e(TAG, "updatePackageParticipants called with null package name");
+            return;
+        }
+
+        // brute force but small code size
+        List<ResolveInfo> services = mPackageManager.queryIntentServices(
+                new Intent(BackupService.SERVICE_ACTION), 0);
+        removePackageParticipantsLockedInner(packageName, services);
+        addPackageParticipantsLockedInner(packageName, services);
+    }
+
+    // ----- IBackupManager binder interface -----
+    
+    public void dataChanged(String packageName) throws RemoteException {
+        // Record that we need a backup pass for the caller.  Since multiple callers
+        // may share a uid, we need to note all candidates within that uid and schedule
+        // a backup pass for each of them.
+
+        Log.d(TAG, "dataChanged packageName=" + packageName);
+        
+        HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
+        Log.d(TAG, "targets=" + targets);
+        if (targets != null) {
+            synchronized (mQueueLock) {
+                // Note that this client has made data changes that need to be backed up
+                for (ServiceInfo service : targets) {
+                    // validate the caller-supplied package name against the known set of
+                    // packages associated with this uid
+                    if (service.packageName.equals(packageName)) {
+                        // Add the caller to the set of pending backups.  If there is
+                        // one already there, then overwrite it, but no harm done.
+                        mPendingBackups.put(new ComponentName(service.packageName, service.name),
+                                new BackupRequest(service, true));
+                        // !!! TODO: write to the pending-backup journal file in case of crash
+                    }
+                }
+
+                Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants");
+                // Schedule a backup pass in a few minutes.  As backup-eligible data
+                // keeps changing, continue to defer the backup pass until things
+                // settle down, to avoid extra overhead.
+                mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
+            }
+        }
+    }
+
+    // Schedule a backup pass for a given package, even if the caller is not part of
+    // that uid or package itself.
+    public void scheduleFullBackup(String packageName) throws RemoteException {
+        // !!! TODO: protect with a signature-or-system permission?
+        HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>();
+        synchronized (mQueueLock) {
+            int numKeys = mBackupParticipants.size();
+            for (int index = 0; index < numKeys; index++) {
+                int uid = mBackupParticipants.keyAt(index);
+                HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
+                for (ServiceInfo service: servicesAtUid) {
+                    if (service.packageName.equals(packageName)) {
+                        mPendingBackups.put(new ComponentName(service.packageName, service.name),
+                                new BackupRequest(service, true));
+                    }
+                }
+            }
+        }
+    }
+
+    
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        synchronized (mQueueLock) {
+            int N = mBackupParticipants.size();
+            pw.println("Participants:");
+            for (int i=0; i<N; i++) {
+                int uid = mBackupParticipants.keyAt(i);
+                pw.print("  uid: ");
+                pw.println(uid);
+                HashSet<ServiceInfo> services = mBackupParticipants.valueAt(i);
+                for (ServiceInfo s: services) {
+                    pw.print("    ");
+                    pw.println(s.toString());
+                }
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 73ff501..90d8c9d 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -84,7 +84,7 @@
     private static final int CRITICAL_BATTERY_LEVEL = 4; 
 
     private static final int DUMP_MAX_LENGTH = 24 * 1024;
-    private static final String[] DUMPSYS_ARGS = new String[] { "-c", "-u" };
+    private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
     private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo";
     
     private static final String DUMPSYS_DATA_PATH = "/data/system/";
@@ -229,6 +229,16 @@
                 EventLog.writeEvent(LOG_BATTERY_LEVEL,
                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
             }
+            if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) {
+                // If the battery level has changed and we are on battery, update the current level.
+                // This is used for discharge cycle tracking so this shouldn't be updated while the 
+                // battery is charging.
+                try {
+                    mBatteryStats.recordCurrentLevel(mBatteryLevel);
+                } catch (RemoteException e) {
+                    // Should never happen.
+                }
+            }
             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
                     mPlugType == BATTERY_PLUGGED_NONE) {
                 // We want to make sure we log discharge cycle outliers
@@ -237,6 +247,20 @@
                 logOutlier = true;
             }
             
+            // Separate broadcast is sent for power connected / not connected
+            // since the standard intent will not wake any applications and some
+            // applications may want to have smart behavior based on this.
+            if (mPlugType != 0 && mLastPlugType == 0) {
+                Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                mContext.sendBroadcast(intent);
+            }
+            else if (mPlugType == 0 && mLastPlugType != 0) {
+                Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                mContext.sendBroadcast(intent);
+            }
+            
             mLastBatteryStatus = mBatteryStatus;
             mLastBatteryHealth = mBatteryHealth;
             mLastBatteryPresent = mBatteryPresent;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 760988d..493bd09 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -35,7 +35,6 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.provider.Sync;
 import android.util.EventLog;
 import android.util.Log;
 
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 85861bb..52e09ca 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -74,7 +74,7 @@
     private boolean mLowMemFlag=false;
     private Context mContext;
     private ContentResolver mContentResolver;
-    int mBlkSize;
+    long mBlkSize;
     long mTotalMemory;
     StatFs mFileStats;
     private static final String DATA_PATH="/data";
@@ -251,7 +251,7 @@
         //initialize block size
         mBlkSize = mFileStats.getBlockSize();
         //initialize total storage on device
-        mTotalMemory = (mFileStats.getBlockCount()*mBlkSize)/100;
+        mTotalMemory = ((long)mFileStats.getBlockCount()*mBlkSize)/100L;
         mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
         mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
         checkMemory(true);
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index c9066be..3fc1e0e 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -21,6 +21,8 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
 import android.os.UEventObserver;
 import android.util.Log;
 import android.media.AudioManager;
@@ -38,15 +40,19 @@
     private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
     private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
 
-    private Context mContext;
-
     private int mHeadsetState;
     private String mHeadsetName;
     private boolean mAudioRouteNeedsUpdate;
     private AudioManager mAudioManager;
 
+    private final Context mContext;
+    private final WakeLock mWakeLock;  // held while there is a pending route change
+
     public HeadsetObserver(Context context) {
         mContext = context;
+        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver");
+        mWakeLock.setReferenceCounted(false);
 
         startObserving(HEADSET_UEVENT_MATCH);
 
@@ -103,6 +109,7 @@
                 // immediate, so delay the route change by 1000ms.
                 // This could be improved once the audio sub-system provides an
                 // interface to clear the audio pipeline.
+                mWakeLock.acquire();
                 mHandler.sendEmptyMessageDelayed(0, 1000);
             } else {
                 updateAudioRoute();
@@ -138,7 +145,8 @@
         @Override
         public void handleMessage(Message msg) {
             updateAudioRoute();
+            mWakeLock.release();
         }
-    };        
+    };
 
 }
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index b534ef1..72efca5 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -89,11 +90,11 @@
         }
     }
 
-    void dumpMap(Printer out, String prefix, Map<String, ArrayList<F>> map) {
+    void dumpMap(PrintWriter out, String prefix, Map<String, ArrayList<F>> map) {
         String eprefix = prefix + "  ";
         String fprefix = prefix + "    ";
         for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
-            out.println(eprefix + e.getKey() + ":");
+            out.print(eprefix); out.print(e.getKey()); out.println(":");
             ArrayList<F> a = e.getValue();
             final int N = a.size();
             for (int i=0; i<N; i++) {
@@ -102,24 +103,25 @@
         }
     }
 
-    public void dump(Printer out, String prefix) {
-        out.println(prefix + "Full MIME Types:");
-        dumpMap(out, prefix+"  ", mTypeToFilter);
-        out.println(prefix);
-        out.println(prefix + "Base MIME Types:");
-        dumpMap(out, prefix+"  ", mBaseTypeToFilter);
-        out.println(prefix);
-        out.println(prefix + "Wild MIME Types:");
-        dumpMap(out, prefix+"  ", mWildTypeToFilter);
-        out.println(prefix);
-        out.println(prefix + "Schemes:");
-        dumpMap(out, prefix+"  ", mSchemeToFilter);
-        out.println(prefix);
-        out.println(prefix + "Non-Data Actions:");
-        dumpMap(out, prefix+"  ", mActionToFilter);
-        out.println(prefix);
-        out.println(prefix + "MIME Typed Actions:");
-        dumpMap(out, prefix+"  ", mTypedActionToFilter);
+    public void dump(PrintWriter out, String prefix) {
+        String innerPrefix = prefix + "  ";
+        out.print(prefix); out.println("Full MIME Types:");
+        dumpMap(out, innerPrefix, mTypeToFilter);
+        out.println(" ");
+        out.print(prefix); out.println("Base MIME Types:");
+        dumpMap(out, innerPrefix, mBaseTypeToFilter);
+        out.println(" ");
+        out.print(prefix); out.println("Wild MIME Types:");
+        dumpMap(out, innerPrefix, mWildTypeToFilter);
+        out.println(" ");
+        out.print(prefix); out.println("Schemes:");
+        dumpMap(out, innerPrefix, mSchemeToFilter);
+        out.println(" ");
+        out.print(prefix); out.println("Non-Data Actions:");
+        dumpMap(out, innerPrefix, mActionToFilter);
+        out.println(" ");
+        out.print(prefix); out.println("MIME Typed Actions:");
+        dumpMap(out, innerPrefix, mTypedActionToFilter);
     }
 
     private class IteratorWrapper implements Iterator<F> {
@@ -275,8 +277,8 @@
         Collections.sort(results, mResolvePrioritySorter);
     }
 
-    protected void dumpFilter(Printer out, String prefix, F filter) {
-        out.println(prefix + filter);
+    protected void dumpFilter(PrintWriter out, String prefix, F filter) {
+        out.print(prefix); out.println(filter);
     }
 
     private final int register_mime_types(F filter, String prefix) {
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 705ddb3..05888e0 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -28,53 +28,52 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
 import java.util.Set;
 import java.util.regex.Pattern;
 
-import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ContentQueryMap;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.Cursor;
 import android.location.Address;
+import android.location.IGeocodeProvider;
 import android.location.IGpsStatusListener;
+import android.location.IGpsStatusProvider;
+import android.location.ILocationCollector;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
+import android.location.ILocationProvider;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
-import android.location.LocationProviderImpl;
 import android.net.ConnectivityManager;
 import android.net.Uri;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.telephony.CellLocation;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
 import android.util.Config;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.SparseIntArray;
 
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.CellState;
 import com.android.internal.location.GpsLocationProvider;
-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.internal.location.LocationProviderProxy;
+import com.android.internal.location.MockProvider;
 import com.android.server.am.BatteryStatsService;
 
 /**
@@ -83,8 +82,7 @@
  *
  * {@hide}
  */
-public class LocationManagerService extends ILocationManager.Stub
-        implements INetworkLocationManager {
+public class LocationManagerService extends ILocationManager.Stub implements Runnable {
     private static final String TAG = "LocationManagerService";
     private static final boolean LOCAL_LOGV = false;
 
@@ -94,10 +92,6 @@
     // Max time to hold wake lock for, in milliseconds.
     private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
 
-    // Time to wait after releasing a wake lock for clients to process location update,
-    // in milliseconds.
-    private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
-
     // The last time a location was written, by provider name.
     private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
 
@@ -111,6 +105,10 @@
         android.Manifest.permission.ACCESS_MOCK_LOCATION;
     private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
         android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
+    private static final String INSTALL_LOCATION_PROVIDER =
+        android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+    private static final String INSTALL_LOCATION_COLLECTOR =
+        android.Manifest.permission.INSTALL_LOCATION_COLLECTOR;
 
     // Set of providers that are explicitly enabled
     private final Set<String> mEnabledProviders = new HashSet<String>();
@@ -119,70 +117,41 @@
     private final Set<String> mDisabledProviders = new HashSet<String>();
 
     // Locations, status values, and extras for mock providers
-    HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
-    private final HashMap<String,Location> mMockProviderLocation = new HashMap<String,Location>();
-    private final HashMap<String,Integer> mMockProviderStatus = new HashMap<String,Integer>();
-    private final HashMap<String,Bundle> mMockProviderStatusExtras = new HashMap<String,Bundle>();
-    private final HashMap<String,Long> mMockProviderStatusUpdateTime = new HashMap<String,Long>();
+    private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
 
     private static boolean sProvidersLoaded = false;
 
     private final Context mContext;
-    private GpsLocationProvider mGpsLocationProvider;
-    private boolean mGpsNavigating;
-    private LocationProviderImpl mNetworkLocationProvider;
-    private INetworkLocationProvider mNetworkLocationInterface;
+    private IGeocodeProvider mGeocodeProvider;
+    private IGpsStatusProvider mGpsStatusProvider;
     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_INSTALL_NETWORK_LOCATION_PROVIDER = 4;
+    private static final int MESSAGE_LOCATION_CHANGED = 1;
 
-    // Alarm manager and wakelock variables
-    private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
+    // wakelock variables
     private final static String WAKELOCK_KEY = "LocationManagerService";
-    private final static String WIFILOCK_KEY = "LocationManagerService";
-    private AlarmManager mAlarmManager;
-    private long mAlarmInterval = 0;
-    private boolean mScreenOn = true;
     private PowerManager.WakeLock mWakeLock = null;
-    private WifiManager.WifiLock mWifiLock = null;
-    private long mWakeLockAcquireTime = 0;
-    private boolean mWakeLockGpsReceived = true;
-    private boolean mWakeLockNetworkReceived = true;
-    private boolean mWifiWakeLockAcquired = false;
-    private boolean mCellWakeLockAcquired = false;
-    
-    private final IBatteryStats mBatteryStats;
+    private int mPendingBroadcasts;
     
     /**
-     * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
+     * List of all receivers.
      */
-    private final ArrayList<Receiver> mListeners = new ArrayList<Receiver>();
+    private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
+
 
     /**
-     * Used for reporting which UIDs are causing the GPS to run.
+     * List of location providers.
      */
-    private final SparseIntArray mReportedGpsUids = new SparseIntArray();
-    private int mReportedGpsSeq = 0;
-    
-    /**
-     * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
-     * This also serves as the lock for our state.
-     */
-    private final HashMap<Receiver,HashMap<String,UpdateRecord>> mLocationListeners =
-        new HashMap<Receiver,HashMap<String,UpdateRecord>>();
+    private final ArrayList<LocationProviderProxy> mProviders =
+        new ArrayList<LocationProviderProxy>();
+    private final HashMap<String, LocationProviderProxy> mProvidersByName
+        = new HashMap<String, LocationProviderProxy>();
 
     /**
-     * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
-     * location.
+     * Object used internally for synchronization
      */
-    private final HashMap<Receiver,HashMap<String,Location>> mLastFixBroadcast =
-        new HashMap<Receiver,HashMap<String,Location>>();
-    private final HashMap<Receiver,HashMap<String,Long>> mLastStatusBroadcast =
-        new HashMap<Receiver,HashMap<String,Long>>();
+    private final Object mLock = new Object();
 
     /**
      * Mapping from provider name to all its UpdateRecords
@@ -190,15 +159,9 @@
     private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
         new HashMap<String,ArrayList<UpdateRecord>>();
 
-    /**
-     * Mappings from provider name to object to use for current location. Locations
-     * contained in this list may not always be valid.
-     */
-    private final HashMap<String,Location> mLocationsByProvider =
-        new HashMap<String,Location>();
-
     // Proximity listeners
-    private Receiver mProximityListener = null;
+    private Receiver mProximityReceiver = null;
+    private ILocationListener mProximityListener = null;
     private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
         new HashMap<PendingIntent,ProximityAlert>();
     private HashSet<ProximityAlert> mProximitiesEntered =
@@ -208,44 +171,34 @@
     private HashMap<String,Location> mLastKnownLocation =
         new HashMap<String,Location>();
 
-    // Battery status extras (from com.android.server.BatteryService)
-    private static final String BATTERY_EXTRA_SCALE = "scale";
-    private static final String BATTERY_EXTRA_LEVEL = "level";
-    private static final String BATTERY_EXTRA_PLUGGED = "plugged";
-
-    // Last known cell service state
-    private TelephonyManager mTelephonyManager;
-
     // Location collector
     private ILocationCollector mCollector;
 
-    // Wifi Manager
-    private WifiManager mWifiManager;
-
     private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
-    private boolean mWifiEnabled = false;
+
+    // for Settings change notification
+    private ContentQueryMap mSettings;
 
     /**
      * A wrapper class holding either an ILocationListener or a PendingIntent to receive
      * location updates.
      */
-    private final class Receiver implements IBinder.DeathRecipient {
+    private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
         final ILocationListener mListener;
         final PendingIntent mPendingIntent;
-        final int mUid;
         final Object mKey;
+        final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
+        int mPendingBroadcasts;
 
-        Receiver(ILocationListener listener, int uid) {
+        Receiver(ILocationListener listener) {
             mListener = listener;
             mPendingIntent = null;
-            mUid = uid;
             mKey = listener.asBinder();
         }
 
-        Receiver(PendingIntent intent, int uid) {
+        Receiver(PendingIntent intent) {
             mPendingIntent = intent;
             mListener = null;
-            mUid = uid;
             mKey = intent;
         }
 
@@ -262,18 +215,17 @@
         public int hashCode() {
             return mKey.hashCode();
         }
-        
-        
+
         @Override
         public String toString() {
             if (mListener != null) {
                 return "Receiver{"
                         + Integer.toHexString(System.identityHashCode(this))
-                        + " uid " + mUid + " Listener " + mKey + "}";
+                        + " Listener " + mKey + "}";
             } else {
                 return "Receiver{"
                         + Integer.toHexString(System.identityHashCode(this))
-                        + " uid " + mUid + " Intent " + mKey + "}";
+                        + " Intent " + mKey + "}";
             }
         }
 
@@ -302,7 +254,16 @@
         public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
             if (mListener != null) {
                 try {
-                    mListener.onStatusChanged(provider, status, extras);
+                    synchronized (this) {
+                        // synchronize to ensure incrementPendingBroadcastsLocked()
+                        // is called before decrementPendingBroadcasts()
+                        mListener.onStatusChanged(provider, status, extras);
+                        if (mListener != mProximityListener) {
+                            // call this after broadcasting so we do not increment
+                            // if we throw an exeption.
+                            incrementPendingBroadcastsLocked();
+                        }
+                    }
                 } catch (RemoteException e) {
                     return false;
                 }
@@ -311,7 +272,14 @@
                 statusChanged.putExtras(extras);
                 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
                 try {
-                    mPendingIntent.send(mContext, 0, statusChanged, null, null);
+                    synchronized (this) {
+                        // synchronize to ensure incrementPendingBroadcastsLocked()
+                        // is called before decrementPendingBroadcasts()
+                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
+                        // call this after broadcasting so we do not increment
+                        // if we throw an exeption.
+                        incrementPendingBroadcastsLocked();
+                    }
                 } catch (PendingIntent.CanceledException e) {
                     return false;
                 }
@@ -322,7 +290,16 @@
         public boolean callLocationChangedLocked(Location location) {
             if (mListener != null) {
                 try {
-                    mListener.onLocationChanged(location);
+                    synchronized (this) {
+                        // synchronize to ensure incrementPendingBroadcastsLocked()
+                        // is called before decrementPendingBroadcasts()
+                        mListener.onLocationChanged(location);
+                        if (mListener != mProximityListener) {
+                            // call this after broadcasting so we do not increment
+                            // if we throw an exeption.
+                            incrementPendingBroadcastsLocked();
+                        }
+                    }
                 } catch (RemoteException e) {
                     return false;
                 }
@@ -330,7 +307,53 @@
                 Intent locationChanged = new Intent();
                 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
                 try {
-                    mPendingIntent.send(mContext, 0, locationChanged, null, null);
+                    synchronized (this) {
+                        // synchronize to ensure incrementPendingBroadcastsLocked()
+                        // is called before decrementPendingBroadcasts()
+                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
+                        // call this after broadcasting so we do not increment
+                        // if we throw an exeption.
+                        incrementPendingBroadcastsLocked();
+                    }
+                } catch (PendingIntent.CanceledException e) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public boolean callProviderEnabledLocked(String provider, boolean enabled) {
+            if (mListener != null) {
+                try {
+                    synchronized (this) {
+                        // synchronize to ensure incrementPendingBroadcastsLocked()
+                        // is called before decrementPendingBroadcasts()
+                        if (enabled) {
+                            mListener.onProviderEnabled(provider);
+                        } else {
+                            mListener.onProviderDisabled(provider);
+                        }
+                        if (mListener != mProximityListener) {
+                            // call this after broadcasting so we do not increment
+                            // if we throw an exeption.
+                            incrementPendingBroadcastsLocked();
+                        }
+                    }
+                } catch (RemoteException e) {
+                    return false;
+                }
+            } else {
+                Intent providerIntent = new Intent();
+                providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
+                try {
+                    synchronized (this) {
+                        // synchronize to ensure incrementPendingBroadcastsLocked()
+                        // is called before decrementPendingBroadcasts()
+                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
+                        // call this after broadcasting so we do not increment
+                        // if we throw an exeption.
+                        incrementPendingBroadcastsLocked();
+                    }
                 } catch (PendingIntent.CanceledException e) {
                     return false;
                 }
@@ -342,9 +365,56 @@
             if (LOCAL_LOGV) {
                 Log.v(TAG, "Location listener died");
             }
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 removeUpdatesLocked(this);
             }
+            synchronized (this) {
+                if (mPendingBroadcasts > 0) {
+                    LocationManagerService.this.decrementPendingBroadcasts();
+                    mPendingBroadcasts = 0;
+                }
+            }
+        }
+
+        public void onSendFinished(PendingIntent pendingIntent, Intent intent,
+                int resultCode, String resultData, Bundle resultExtras) {
+            synchronized (this) {
+                decrementPendingBroadcastsLocked();
+            }
+        }
+
+        // this must be called while synchronized by caller in a synchronized block
+        // containing the sending of the broadcaset
+        private void incrementPendingBroadcastsLocked() {
+            if (mPendingBroadcasts++ == 0) {
+                LocationManagerService.this.incrementPendingBroadcasts();
+            }
+        }
+
+        private void decrementPendingBroadcastsLocked() {
+            if (--mPendingBroadcasts == 0) {
+                LocationManagerService.this.decrementPendingBroadcasts();
+            }
+        }
+    }
+
+    public void locationCallbackFinished(ILocationListener listener) {
+        Receiver receiver = getReceiver(listener);
+        if (receiver != null) {
+            synchronized (receiver) {
+                // so wakelock calls will succeed
+                long identity = Binder.clearCallingIdentity();
+                receiver.decrementPendingBroadcastsLocked();
+                Binder.restoreCallingIdentity(identity);
+           }
+        }
+    }
+
+    private final class SettingsObserver implements Observer {
+        public void update(Observable o, Object arg) {
+            synchronized (mLock) {
+                updateProvidersLocked();
+            }
         }
     }
 
@@ -439,17 +509,18 @@
         }
     }
 
-    /**
-     * Load providers from /data/location/<provider_name>/
-     *                                                          class
-     *                                                          kml
-     *                                                          nmea
-     *                                                          track
-     *                                                          location
-     *                                                          properties
-     */
+    private void addProvider(LocationProviderProxy provider) {
+        mProviders.add(provider);
+        mProvidersByName.put(provider.getName(), provider);
+    }
+
+    private void removeProvider(LocationProviderProxy provider) {
+        mProviders.remove(provider);
+        mProvidersByName.remove(provider.getName());
+    }
+
     private void loadProviders() {
-        synchronized (mLocationListeners) {
+        synchronized (mLock) {
             if (sProvidersLoaded) {
                 return;
             }
@@ -472,73 +543,10 @@
         // Attempt to load "real" providers first
         if (GpsLocationProvider.isSupported()) {
             // Create a gps location provider
-            mGpsLocationProvider = new GpsLocationProvider(mContext);
-            LocationProviderImpl.addProvider(mGpsLocationProvider);
-        }
-
-        // Load fake providers if real providers are not available
-        File f = new File(LocationManager.PROVIDER_DIR);
-        if (f.isDirectory()) {
-            File[] subdirs = f.listFiles();
-            for (int i = 0; i < subdirs.length; i++) {
-                if (!subdirs[i].isDirectory()) {
-                    continue;
-                }
-
-                String name = subdirs[i].getName();
-
-                if (LOCAL_LOGV) {
-                    Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
-                    Log.v(TAG, "name = " + name);
-                }
-
-                // Don't create a fake provider if a real provider exists
-                if (LocationProviderImpl.getProvider(name) == null) {
-                    LocationProviderImpl provider = null;
-                    try {
-                        File classFile = new File(subdirs[i], "class");
-                        // Look for a 'class' file
-                        provider = LocationProviderImpl.loadFromClass(classFile);
-
-                        // Look for an 'kml', 'nmea', or 'track' file
-                        if (provider == null) {
-                            // Load properties from 'properties' file, if present
-                            File propertiesFile = new File(subdirs[i], "properties");
-
-                            if (propertiesFile.exists()) {
-                                provider = new TrackProvider(name);
-                                ((TrackProvider)provider).readProperties(propertiesFile);
-
-                                File kmlFile = new File(subdirs[i], "kml");
-                                if (kmlFile.exists()) {
-                                    ((TrackProvider) provider).readKml(kmlFile);
-                                } else {
-                                    File nmeaFile = new File(subdirs[i], "nmea");
-                                    if (nmeaFile.exists()) {
-                                        ((TrackProvider) provider).readNmea(name, nmeaFile);
-                                    } else {
-                                        File trackFile = new File(subdirs[i], "track");
-                                        if (trackFile.exists()) {
-                                            ((TrackProvider) provider).readTrack(trackFile);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        if (provider != null) {
-                            LocationProviderImpl.addProvider(provider);
-                        }
-                        // Grab the initial location of a TrackProvider and
-                        // store it as the last known location for that provider
-                        if (provider instanceof TrackProvider) {
-                            TrackProvider tp = (TrackProvider) provider;
-                            mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
-                        }
-                    } catch (Exception e) {
-                        Log.e(TAG, "Exception loading provder " + name, e);
-                    }
-                }
-            }
+            GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
+            mGpsStatusProvider = provider.getGpsStatusProvider();
+            LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
+            addProvider(proxy);
         }
 
         updateProvidersLocked();
@@ -550,118 +558,95 @@
     public LocationManagerService(Context context) {
         super();
         mContext = context;
-        mLocationHandler = new LocationWorkerHandler();
+
+        Thread thread = new Thread(null, this, "LocationManagerService");
+        thread.start();
 
         if (LOCAL_LOGV) {
             Log.v(TAG, "Constructed LocationManager Service");
         }
+    }
 
-        // Alarm manager, needs to be done before calling loadProviders() below
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
+    private void initialize() {
         // 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();
 
-        // Listen for Radio changes
-        mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
-        mTelephonyManager.listen(mPhoneStateListener,
-                PhoneStateListener.LISTEN_CELL_LOCATION |
-                PhoneStateListener.LISTEN_SIGNAL_STRENGTH |
-                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
-
         // Register for Network (Wifi or Mobile) updates
-        NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
-        IntentFilter networkIntentFilter = new IntentFilter();
-        networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-        networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
-        context.registerReceiver(networkReceiver, networkIntentFilter);
-
-        // Register for power updates
-        PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
         IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ALARM_INTENT);
-        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        // Register for Package Manager updates
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        context.registerReceiver(powerStateReceiver, intentFilter);
+        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
 
-        // Get the wifi manager
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-
-        // Create a wifi lock for future use
-        mWifiLock = getWifiWakelockLocked();
+        // listen for settings changes
+        ContentResolver resolver = mContext.getContentResolver();
+        Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
+                "(" + Settings.System.NAME + "=?)",
+                new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
+                null);
+        mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
+        SettingsObserver settingsObserver = new SettingsObserver();
+        mSettings.addObserver(settingsObserver);
     }
 
-    public void setInstallCallback(InstallCallback callback) {
-        synchronized (mLocationListeners) {
-            mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER);
-            Message m = Message.obtain(mLocationHandler, 
-                    MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback);
-            mLocationHandler.sendMessageAtFrontOfQueue(m);
+    public void run()
+    {
+        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        Looper.prepare();
+        mLocationHandler = new LocationWorkerHandler();
+        initialize();
+        Looper.loop();
+    }
+
+    public void installLocationProvider(String name, ILocationProvider provider) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
         }
-    }
 
-    public void setNetworkLocationProvider(INetworkLocationProvider provider) {
-        synchronized (mLocationListeners) {
-            mNetworkLocationInterface = provider;
-            provider.addListener(getPackageNames());
-            mNetworkLocationProvider = (LocationProviderImpl)provider;
-            LocationProviderImpl.addProvider(mNetworkLocationProvider);
+        synchronized (mLock) {
+            // check to see if we are reinstalling a dead provider
+            LocationProviderProxy oldProvider = mProvidersByName.get(name);
+            if (oldProvider != null) {
+                if (oldProvider.isDead()) {
+                    Log.d(TAG, "replacing dead provider");
+                    removeProvider(oldProvider);
+                } else {
+                    throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
+                }
+            }
+
+            LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
+            addProvider(proxy);
             updateProvidersLocked();
-            
-            // notify NetworkLocationProvider of any events it might have missed
-            synchronized (mLocationListeners) {
-                mNetworkLocationProvider.updateNetworkState(mNetworkState);
-                mNetworkLocationInterface.updateWifiEnabledState(mWifiEnabled);
-                mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
 
-                if (mLastCellState != null) {
-                    if (mCollector != null) {
-                        mCollector.updateCellState(mLastCellState);
-                    }
-                    mNetworkLocationProvider.updateCellState(mLastCellState);
-                }
-
-                // There might be an existing wifi scan available
-                if (mWifiManager != null) {
-                    List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
-                    if (wifiScanResults != null && wifiScanResults.size() != 0) {
-                        mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
-                        if (mCollector != null) {
-                            mCollector.updateWifiScanResults(wifiScanResults);
-                        }
-                    }
-                }
-            }
+            // notify provider of current network state
+            proxy.updateNetworkState(mNetworkState);
         }
     }
 
-    public void setLocationCollector(ILocationCollector collector) {
-        synchronized (mLocationListeners) {
-            mCollector = collector;
-            if (mGpsLocationProvider != null) {
-                mGpsLocationProvider.setLocationCollector(mCollector);
-            }
+    public void installLocationCollector(ILocationCollector collector) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission");
         }
+
+        // FIXME - only support one collector
+        mCollector = collector;
     }
 
-    private WifiManager.WifiLock getWifiWakelockLocked() {
-        if (mWifiLock == null && mWifiManager != null) {
-            mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
-            mWifiLock.setReferenceCounted(false);
+    public void installGeocodeProvider(IGeocodeProvider provider) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
         }
-        return mWifiLock;
+
+        mGeocodeProvider = provider;
     }
 
     private boolean isAllowedBySettingsLocked(String provider) {
@@ -712,15 +697,9 @@
         return true;
     }
 
-    private String[] getPackageNames() {
-        // Since a single UID may correspond to multiple packages, this can only be used as an
-        // approximation for tracking
-        return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
-    }
-
     public List<String> getAllProviders() {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 return _getAllProvidersLocked();
             }
         } catch (SecurityException se) {
@@ -735,10 +714,9 @@
         if (LOCAL_LOGV) {
             Log.v(TAG, "getAllProviders");
         }
-        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
-        ArrayList<String> out = new ArrayList<String>(providers.size());
-
-        for (LocationProviderImpl p : providers) {
+        ArrayList<String> out = new ArrayList<String>(mProviders.size());
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            LocationProviderProxy p = mProviders.get(i);
             out.add(p.getName());
         }
         return out;
@@ -746,7 +724,7 @@
 
     public List<String> getProviders(boolean enabledOnly) {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 return _getProvidersLocked(enabledOnly);
             }
         } catch (SecurityException se) {
@@ -761,10 +739,9 @@
         if (LOCAL_LOGV) {
             Log.v(TAG, "getProviders");
         }
-        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
-        ArrayList<String> out = new ArrayList<String>();
-
-        for (LocationProviderImpl p : providers) {
+        ArrayList<String> out = new ArrayList<String>(mProviders.size());
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            LocationProviderProxy p = mProviders.get(i);
             String name = p.getName();
             if (isAllowedProviderSafe(name)) {
                 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
@@ -776,24 +753,13 @@
         return out;
     }
 
-    public void updateProviders() {
-        synchronized (mLocationListeners) {
-            updateProvidersLocked();
-        }
-    }
-
     private void updateProvidersLocked() {
-        for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            LocationProviderProxy p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
             boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
 
-            // Collection is only allowed when network provider is being used
-            if (mCollector != null &&
-                    p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
-                mCollector.updateNetworkProviderStatus(shouldBeEnabled);
-            }
-
             if (isEnabled && !shouldBeEnabled) {
                 updateProviderListenersLocked(name, false);
             } else if (!isEnabled && shouldBeEnabled) {
@@ -806,7 +772,7 @@
     private void updateProviderListenersLocked(String provider, boolean enabled) {
         int listeners = 0;
 
-        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
+        LocationProviderProxy p = mProvidersByName.get(provider);
         if (p == null) {
             return;
         }
@@ -819,29 +785,11 @@
             for (int i=0; i<N; i++) {
                 UpdateRecord record = records.get(i);
                 // Sends a notification message to the receiver
-                try {
-                    Receiver receiver = record.mReceiver;
-                    if (receiver.isListener()) {
-                        if (enabled) {
-                            receiver.getListener().onProviderEnabled(provider);
-                        } else {
-                            receiver.getListener().onProviderDisabled(provider);
-                        }
-                    } else {
-                        Intent providerIntent = new Intent();
-                        providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
-                        try {
-                            receiver.getPendingIntent().send(mContext, 0,
-                                 providerIntent, null, null);
-                        } catch (PendingIntent.CanceledException e) {
-                            if (deadReceivers == null) {
-                                deadReceivers = new ArrayList<Receiver>();
-                                deadReceivers.add(receiver);
-                            }
-                        }
+                if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
+                    if (deadReceivers == null) {
+                        deadReceivers = new ArrayList<Receiver>();
+                        deadReceivers.add(record.mReceiver);
                     }
-                } catch (RemoteException e) {
-                    // The death link will clean this up.
                 }
                 listeners++;
             }
@@ -858,24 +806,10 @@
             if (listeners > 0) {
                 p.setMinTime(getMinTimeLocked(provider));
                 p.enableLocationTracking(true);
-                updateWakelockStatusLocked(mScreenOn);
             }
         } else {
             p.enableLocationTracking(false);
-            if (p == mGpsLocationProvider) {
-                mGpsNavigating = false;
-                reportStopGpsLocked();
-            }
             p.disable();
-            updateWakelockStatusLocked(mScreenOn);
-        }
-
-        if (enabled && listeners > 0) {
-            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
-            Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
-            mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
-        } else {
-            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
         }
     }
 
@@ -896,19 +830,19 @@
         final long mMinTime;
         final float mMinDistance;
         final int mUid;
-        final String[] mPackages;
+        Location mLastFixBroadcast;
+        long mLastStatusBroadcast;
 
         /**
          * Note: must be constructed with lock held.
          */
         UpdateRecord(String provider, long minTime, float minDistance,
-            Receiver receiver, int uid, String[] packages) {
+            Receiver receiver, int uid) {
             mProvider = provider;
             mReceiver = receiver;
             mMinTime = minTime;
             mMinDistance = minDistance;
             mUid = uid;
-            mPackages = packages;
 
             ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
             if (records == null) {
@@ -940,33 +874,74 @@
             pw.println(prefix + this);
             pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
             pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
-            StringBuilder sb = new StringBuilder();
-            if (mPackages != null) {
-                for (int i=0; i<mPackages.length; i++) {
-                    if (i > 0) sb.append(", ");
-                    sb.append(mPackages[i]);
-                }
-            }
-            pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb);
+            pw.println(prefix + "mUid=" + mUid);
+            pw.println(prefix + "mLastFixBroadcast:");
+            mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + "  ");
+            pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
         }
         
         /**
          * Calls dispose().
          */
         @Override protected void finalize() {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 disposeLocked();
             }
         }
     }
 
+    private Receiver getReceiver(ILocationListener listener) {
+        IBinder binder = listener.asBinder();
+        Receiver receiver = mReceivers.get(binder);
+        if (receiver == null) {
+            receiver = new Receiver(listener);
+            mReceivers.put(binder, receiver);
+
+            try {
+                if (receiver.isListener()) {
+                    receiver.getListener().asBinder().linkToDeath(receiver, 0);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "linkToDeath failed:", e);
+                return null;
+            }
+        }
+        return receiver;
+    }
+
+    private Receiver getReceiver(PendingIntent intent) {
+        Receiver receiver = mReceivers.get(intent);
+        if (receiver == null) {
+            receiver = new Receiver(intent);
+            mReceivers.put(intent, receiver);
+        }
+        return receiver;
+    }
+
+    private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
+        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+        if (records != null) {
+            for (int i = records.size() - 1; i >= 0; i--) {
+                UpdateRecord record = records.get(i);
+                if (record.mUid == uid && record.mReceiver != excludedReceiver) {
+                    return true;
+                }
+           }
+        }
+        for (ProximityAlert alert : mProximityAlerts.values()) {
+            if (alert.mUid == uid) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public void requestLocationUpdates(String provider,
         long minTime, float minDistance, ILocationListener listener) {
 
         try {
-            synchronized (mLocationListeners) {
-                requestLocationUpdatesLocked(provider, minTime, minDistance,
-                    new Receiver(listener, Binder.getCallingUid()));
+            synchronized (mLock) {
+                requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
             }
         } catch (SecurityException se) {
             throw se;
@@ -978,9 +953,8 @@
     public void requestLocationUpdatesPI(String provider,
             long minTime, float minDistance, PendingIntent intent) {
         try {
-            synchronized (mLocationListeners) {
-                requestLocationUpdatesLocked(provider, minTime, minDistance,
-                        new Receiver(intent, Binder.getCallingUid()));
+            synchronized (mLock) {
+                requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
             }
         } catch (SecurityException se) {
             throw se;
@@ -995,69 +969,36 @@
             Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
         }
 
-        LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
-        if (impl == null) {
+        LocationProviderProxy proxy = mProvidersByName.get(provider);
+        if (proxy == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
 
         checkPermissionsSafe(provider);
 
-        String[] packages = getPackageNames();
-
         // so wakelock calls will succeed
         final int callingUid = Binder.getCallingUid();
+        boolean newUid = !providerHasListener(provider, callingUid, null);
         long identity = Binder.clearCallingIdentity();
         try {
-            UpdateRecord r = new UpdateRecord(provider, minTime, minDistance,
-                    receiver, callingUid, packages);
-            if (!mListeners.contains(receiver)) {
-                try {
-                    if (receiver.isListener()) {
-                        receiver.getListener().asBinder().linkToDeath(receiver, 0);
-                    }
-                    mListeners.add(receiver);
-                } catch (RemoteException e) {
-                    return;
-                }
-            }
-
-            HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver);
-            if (records == null) {
-                records = new HashMap<String,UpdateRecord>();
-                mLocationListeners.put(receiver, records);
-            }
-            UpdateRecord oldRecord = records.put(provider, r);
+            UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
+            UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
             if (oldRecord != null) {
                 oldRecord.disposeLocked();
             }
 
+            if (newUid) {
+                proxy.addListener(callingUid);
+            }
+
             boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
             if (isProviderEnabled) {
                 long minTimeForProvider = getMinTimeLocked(provider);
-                impl.setMinTime(minTimeForProvider);
-                impl.enableLocationTracking(true);
-                updateWakelockStatusLocked(mScreenOn);
-
-                if (provider.equals(LocationManager.GPS_PROVIDER)) {
-                    if (mGpsNavigating) {
-                        updateReportedGpsLocked();
-                    }
-                }
-                
-                // Clear heartbeats if any before starting a new one
-                mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
-                Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
-                mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
+                proxy.setMinTime(minTimeForProvider);
+                proxy.enableLocationTracking(true);
             } else {
-                try {
-                    // Notify the listener that updates are currently disabled
-                    if (receiver.isListener()) {
-                        receiver.getListener().onProviderDisabled(provider);
-                    }
-                } catch(RemoteException e) {
-                    Log.w(TAG, "RemoteException calling onProviderDisabled on " +
-                            receiver.getListener());
-                }
+                // Notify the listener that updates are currently disabled
+                receiver.callProviderEnabledLocked(provider, false);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -1066,8 +1007,8 @@
 
     public void removeUpdates(ILocationListener listener) {
         try {
-            synchronized (mLocationListeners) {
-                removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid()));
+            synchronized (mLock) {
+                removeUpdatesLocked(getReceiver(listener));
             }
         } catch (SecurityException se) {
             throw se;
@@ -1078,8 +1019,8 @@
 
     public void removeUpdatesPI(PendingIntent intent) {
         try {
-            synchronized (mLocationListeners) {
-                removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid()));
+            synchronized (mLock) {
+                removeUpdatesLocked(getReceiver(intent));
             }
         } catch (SecurityException se) {
             throw se;
@@ -1097,23 +1038,20 @@
         final int callingUid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
-            int idx = mListeners.indexOf(receiver);
-            if (idx >= 0) {
-                Receiver myReceiver = mListeners.remove(idx);
-                if (myReceiver.isListener()) {
-                    myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
-                }
+            if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
+                receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
             }
 
             // Record which providers were associated with this listener
             HashSet<String> providers = new HashSet<String>();
-            HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver);
+            HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
             if (oldRecords != null) {
                 // Call dispose() on the obsolete update records.
                 for (UpdateRecord record : oldRecords.values()) {
-                    if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
-                        if (mNetworkLocationInterface != null) {
-                            mNetworkLocationInterface.removeListener(record.mPackages);
+                    if (!providerHasListener(record.mProvider, callingUid, receiver)) {
+                        LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
+                        if (proxy != null) {
+                            proxy.removeListener(callingUid);
                         }
                     }
                     record.disposeLocked();
@@ -1121,10 +1059,6 @@
                 // Accumulate providers
                 providers.addAll(oldRecords.keySet());
             }
-            
-            mLocationListeners.remove(receiver);
-            mLastFixBroadcast.remove(receiver);
-            mLastStatusBroadcast.remove(receiver);
 
             // See if the providers associated with this listener have any
             // other listeners; if one does, inform it of the new smallest minTime
@@ -1141,29 +1075,22 @@
                     hasOtherListener = true;
                 }
 
-                LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
+                LocationProviderProxy p = mProvidersByName.get(provider);
                 if (p != null) {
                     if (hasOtherListener) {
                         p.setMinTime(getMinTimeLocked(provider));
                     } else {
-                        mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
                         p.enableLocationTracking(false);
                     }
-                    
-                    if (p == mGpsLocationProvider && mGpsNavigating) {
-                        updateReportedGpsLocked();
-                    }
                 }
             }
-
-            updateWakelockStatusLocked(mScreenOn);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     public boolean addGpsStatusListener(IGpsStatusListener listener) {
-        if (mGpsLocationProvider == null) {
+        if (mGpsStatusProvider == null) {
             return false;
         }
         if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
@@ -1172,17 +1099,21 @@
         }
 
         try {
-            mGpsLocationProvider.addGpsStatusListener(listener);
+            mGpsStatusProvider.addGpsStatusListener(listener);
         } catch (RemoteException e) {
-            Log.w(TAG, "RemoteException in addGpsStatusListener");
+            Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
             return false;
         }
         return true;
     }
 
     public void removeGpsStatusListener(IGpsStatusListener listener) {
-        synchronized (mLocationListeners) {
-            mGpsLocationProvider.removeGpsStatusListener(listener);
+        synchronized (mLock) {
+            try {
+                mGpsStatusProvider.removeGpsStatusListener(listener);
+            } catch (Exception e) {
+                Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
+            }
         }
     }
 
@@ -1195,13 +1126,13 @@
             throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
         }
 
-        synchronized (mLocationListeners) {
-            LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
+        synchronized (mLock) {
+            LocationProviderProxy proxy = mProvidersByName.get(provider);
             if (provider == null) {
                 return false;
             }
     
-            return impl.sendExtraCommand(command, extras);
+            return proxy.sendExtraCommand(command, extras);
         }
     }
 
@@ -1263,7 +1194,7 @@
     }
 
     // Listener for receiving locations to trigger proximity alerts
-    class ProximityListener extends ILocationListener.Stub {
+    class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
 
         boolean isGpsAvailable = false;
 
@@ -1300,7 +1231,14 @@
                         Intent enteredIntent = new Intent();
                         enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
                         try {
-                            intent.send(mContext, 0, enteredIntent, null, null);
+                            synchronized (this) {
+                                // synchronize to ensure incrementPendingBroadcasts()
+                                // is called before decrementPendingBroadcasts()
+                                intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
+                                // call this after broadcasting so we do not increment
+                                // if we throw an exeption.
+                                incrementPendingBroadcasts();
+                            }
                         } catch (PendingIntent.CanceledException e) {
                             if (LOCAL_LOGV) {
                                 Log.v(TAG, "Canceled proximity alert: " + alert, e);
@@ -1318,7 +1256,14 @@
                         Intent exitedIntent = new Intent();
                         exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
                         try {
-                            intent.send(mContext, 0, exitedIntent, null, null);
+                            synchronized (this) {
+                                // synchronize to ensure incrementPendingBroadcasts()
+                                // is called before decrementPendingBroadcasts()
+                                intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
+                                // call this after broadcasting so we do not increment
+                                // if we throw an exeption.
+                                incrementPendingBroadcasts();
+                            }
                         } catch (PendingIntent.CanceledException e) {
                             if (LOCAL_LOGV) {
                                 Log.v(TAG, "Canceled proximity alert: " + alert, e);
@@ -1371,12 +1316,21 @@
                 isGpsAvailable = false;
             }
         }
+
+        public void onSendFinished(PendingIntent pendingIntent, Intent intent,
+                int resultCode, String resultData, Bundle resultExtras) {
+            // synchronize to ensure incrementPendingBroadcasts()
+            // is called before decrementPendingBroadcasts()
+            synchronized (this) {
+                decrementPendingBroadcasts();
+            }
+        }
     }
 
     public void addProximityAlert(double latitude, double longitude,
         float radius, long expiration, PendingIntent intent) {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
             }
         } catch (SecurityException se) {
@@ -1408,28 +1362,20 @@
                 latitude, longitude, radius, expiration, intent);
         mProximityAlerts.put(intent, alert);
 
-        if (mProximityListener == null) {
-            mProximityListener = new Receiver(new ProximityListener(), -1);
+        if (mProximityReceiver == null) {
+            mProximityListener = new ProximityListener();
+            mProximityReceiver = new Receiver(mProximityListener);
 
-            LocationProvider provider = LocationProviderImpl.getProvider(
-                LocationManager.GPS_PROVIDER);
-            if (provider != null) {
-                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
+            for (int i = mProviders.size() - 1; i >= 0; i--) {
+                LocationProviderProxy provider = mProviders.get(i);
+                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
             }
-
-            provider =
-                LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
-            if (provider != null) {
-                requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
-            }
-        } else if (mGpsNavigating) {
-            updateReportedGpsLocked();
         }
     }
 
     public void removeProximityAlert(PendingIntent intent) {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                removeProximityAlertLocked(intent);
             }
         } catch (SecurityException se) {
@@ -1446,10 +1392,9 @@
 
         mProximityAlerts.remove(intent);
         if (mProximityAlerts.size() == 0) {
-            removeUpdatesLocked(mProximityListener);
+            removeUpdatesLocked(mProximityReceiver);
+            mProximityReceiver = null;
             mProximityListener = null;
-        } else if (mGpsNavigating) {
-            updateReportedGpsLocked();
         }
      }
 
@@ -1460,7 +1405,7 @@
      */
     public Bundle getProviderInfo(String provider) {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 return _getProviderInfoLocked(provider);
             }
         } catch (SecurityException se) {
@@ -1472,7 +1417,7 @@
     }
 
     private Bundle _getProviderInfoLocked(String provider) {
-        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
+        LocationProviderProxy p = mProvidersByName.get(provider);
         if (p == null) {
             return null;
         }
@@ -1495,7 +1440,7 @@
 
     public boolean isProviderEnabled(String provider) {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 return _isProviderEnabledLocked(provider);
             }
         } catch (SecurityException se) {
@@ -1506,10 +1451,21 @@
         }
     }
 
+    public void reportLocation(Location location) {
+        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
+        }
+
+        mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
+        Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
+        mLocationHandler.sendMessageAtFrontOfQueue(m);
+    }
+
     private boolean _isProviderEnabledLocked(String provider) {
         checkPermissionsSafe(provider);
 
-        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
+        LocationProviderProxy p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
@@ -1518,7 +1474,7 @@
 
     public Location getLastKnownLocation(String provider) {
         try {
-            synchronized (mLocationListeners) {
+            synchronized (mLock) {
                 return _getLastKnownLocationLocked(provider);
             }
         } catch (SecurityException se) {
@@ -1532,7 +1488,7 @@
     private Location _getLastKnownLocationLocked(String provider) {
         checkPermissionsSafe(provider);
 
-        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
+        LocationProviderProxy p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
@@ -1576,79 +1532,34 @@
         return true;
     }
 
-    private void handleLocationChangedLocked(String provider) {
+    private void handleLocationChangedLocked(Location location) {
+        String provider = location.getProvider();
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
         if (records == null || records.size() == 0) {
             return;
         }
 
-        LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
+        LocationProviderProxy p = mProvidersByName.get(provider);
         if (p == null) {
             return;
         }
 
-        // Get location object
-        Location loc = mLocationsByProvider.get(provider);
-        if (loc == null) {
-            loc = new Location(provider);
-            mLocationsByProvider.put(provider, loc);
-        } else {
-            loc.reset();
-        }
-
-        // Use the mock location if available
-        Location mockLoc = mMockProviderLocation.get(provider);
-        boolean locationValid;
-        if (mockLoc != null) {
-            locationValid = true;
-            loc.set(mockLoc);
-        } else {
-            locationValid = p.getLocation(loc);
-        }
-
         // Update last known location for provider
-        if (locationValid) {
-            Location location = mLastKnownLocation.get(provider);
-            if (location == null) {
-                mLastKnownLocation.put(provider, new Location(loc));
-            } else {
-                location.set(loc);
-            }
-            writeLastKnownLocationLocked(provider, loc);
-
-            if (p instanceof INetworkLocationProvider) {
-                mWakeLockNetworkReceived = true;
-            } else if (p instanceof GpsLocationProvider) {
-                // Gps location received signal is in NetworkStateBroadcastReceiver
-            }
+        Location lastLocation = mLastKnownLocation.get(provider);
+        if (lastLocation == null) {
+            mLastKnownLocation.put(provider, new Location(location));
+        } else {
+            lastLocation.set(location);
         }
+        writeLastKnownLocationLocked(provider, location);
 
         // Fetch latest status update time
         long newStatusUpdateTime = p.getStatusUpdateTime();
 
-        // Override real time with mock time if present
-        Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
-        if (mockStatusUpdateTime != null) {
-            newStatusUpdateTime = mockStatusUpdateTime.longValue();
-        }
-
-        // Get latest status
+       // Get latest status
         Bundle extras = new Bundle();
         int status = p.getStatus(extras);
 
-        // Override status with mock status if present
-        Integer mockStatus = mMockProviderStatus.get(provider);
-        if (mockStatus != null) {
-            status = mockStatus.intValue();
-        }
-
-        // Override extras with mock extras if present
-        Bundle mockExtras = mMockProviderStatusExtras.get(provider);
-        if (mockExtras != null) {
-            extras.clear();
-            extras.putAll(mockExtras);
-        }
-
         ArrayList<Receiver> deadReceivers = null;
         
         // Broadcast location or status to all listeners
@@ -1657,44 +1568,28 @@
             UpdateRecord r = records.get(i);
             Receiver receiver = r.mReceiver;
 
-            // Broadcast location only if it is valid
-            if (locationValid) {
-                HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
-                if (map == null) {
-                    map = new HashMap<String,Location>();
-                    mLastFixBroadcast.put(receiver, map);
+            Location lastLoc = r.mLastFixBroadcast;
+            if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
+                if (lastLoc == null) {
+                    lastLoc = new Location(location);
+                    r.mLastFixBroadcast = lastLoc;
+                } else {
+                    lastLoc.set(location);
                 }
-                Location lastLoc = map.get(provider);
-                if ((lastLoc == null) || shouldBroadcastSafe(loc, lastLoc, r)) {
-                    if (lastLoc == null) {
-                        lastLoc = new Location(loc);
-                        map.put(provider, lastLoc);
-                    } else {
-                        lastLoc.set(loc);
+                if (!receiver.callLocationChangedLocked(location)) {
+                    Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
+                    if (deadReceivers == null) {
+                        deadReceivers = new ArrayList<Receiver>();
                     }
-                    if (!receiver.callLocationChangedLocked(loc)) {
-                        Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
-                        if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<Receiver>();
-                        }
-                        deadReceivers.add(receiver);
-                    }
+                    deadReceivers.add(receiver);
                 }
             }
 
-            // Broadcast status message
-            HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver);
-            if (statusMap == null) {
-                statusMap = new HashMap<String,Long>();
-                mLastStatusBroadcast.put(receiver, statusMap);
-            }
-            long prevStatusUpdateTime =
-                (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
-
+            long prevStatusUpdateTime = r.mLastStatusBroadcast;
             if ((newStatusUpdateTime > prevStatusUpdateTime) &&
                 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
 
-                statusMap.put(provider, newStatusUpdateTime);
+                r.mLastStatusBroadcast = newStatusUpdateTime;
                 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
                     Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
                     if (deadReceivers == null) {
@@ -1719,70 +1614,28 @@
         @Override
         public void handleMessage(Message msg) {
             try {
-                if (msg.what == MESSAGE_HEARTBEAT) {
-                    // log("LocationWorkerHandler: Heartbeat!");
+                if (msg.what == MESSAGE_LOCATION_CHANGED) {
+                    // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
 
-                    synchronized (mLocationListeners) {
-                        String provider = (String) msg.obj;
+                    synchronized (mLock) {
+                        Location location = (Location) msg.obj;
+
+                        if (mCollector != null && 
+                                LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
+                            try {
+                                mCollector.updateLocation(location);
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "mCollector.updateLocation failed");
+                                mCollector = null;
+                            }
+                        }
+
+                        String provider = location.getProvider();
                         if (!isAllowedBySettingsLocked(provider)) {
                             return;
                         }
 
-                        // Process the location fix if the screen is on or we're holding a wakelock
-                        if (mScreenOn || (mWakeLockAcquireTime != 0)) {
-                            handleLocationChangedLocked(provider);
-                        }
-
-                        // If it continues to have listeners
-                        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
-                        if (records != null && records.size() > 0) {
-                            Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
-                            sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
-                        }
-
-                        if ((mWakeLockAcquireTime != 0) &&
-                            (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
-                                > MAX_TIME_FOR_WAKE_LOCK)) {
-    
-                            removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
-                            removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
-    
-                            log("LocationWorkerHandler: Exceeded max time for wake lock");
-                            Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
-                            sendMessageAtFrontOfQueue(m);
-    
-                        } else if (mWakeLockAcquireTime != 0 &&
-                            mWakeLockGpsReceived && mWakeLockNetworkReceived) {
-    
-                            removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
-                            removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
-    
-                            log("LocationWorkerHandler: Locations received.");
-                            mWakeLockAcquireTime = 0;
-                            Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
-                            sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
-                        }
-                    }
-
-                } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
-                    log("LocationWorkerHandler: Acquire");
-                    synchronized (mLocationListeners) {
-                        acquireWakeLockLocked();
-                    }
-                } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
-                    log("LocationWorkerHandler: Release");
-
-                    // Update wakelock status so the next alarm is set before releasing wakelock
-                    synchronized (mLocationListeners) {
-                        updateWakelockStatusLocked(mScreenOn);
-                        releaseWakeLockLocked();
-                    }
-                } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) {
-                    synchronized (mLocationListeners) {
-                        Log.d(TAG, "installing network location provider");
-                        INetworkLocationManager.InstallCallback callback =
-                                (INetworkLocationManager.InstallCallback)msg.obj;
-                        callback.installNetworkLocationProvider(LocationManagerService.this);
+                        handleLocationChangedLocked(location);
                     }
                 }
             } catch (Exception e) {
@@ -1792,159 +1645,14 @@
         }
     }
 
-    class CellLocationUpdater extends Thread {
-        CellLocation mNextLocation;
-        
-        CellLocationUpdater() {
-            super("CellLocationUpdater");
-        }
-        
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
-        public void run() {
-            int curAsu = -1;
-            CellLocation curLocation = null;
-            
-            while (true) {
-                // See if there is more work to do...
-                synchronized (mLocationListeners) {
-                    if (curLocation == mNextLocation) {
-                        mCellLocationUpdater = null;
-                        break;
-                    }
-                    
-                    curLocation = mNextLocation;
-                    if (curLocation == null) {
-                        mCellLocationUpdater = null;
-                        break;
-                    }
-                    
-                    curAsu = mLastSignalStrength;
-                    
-                    mNextLocation = null;
-                }
-                
-                try {
-                    // Gets cell state.  This can block so must be done without
-                    // locks held.
-                    CellState cs = new CellState(mTelephonyManager, curLocation, curAsu);
-                    
-                    synchronized (mLocationListeners) {
-                        mLastCellState = cs;
-        
-                        cs.updateSignalStrength(mLastSignalStrength);
-                        cs.updateRadioType(mLastRadioType);
-                        
-                        // Notify collector
-                        if (mCollector != null) {
-                            mCollector.updateCellState(cs);
-                        }
-    
-                        // Updates providers
-                        List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
-                        for (LocationProviderImpl provider : providers) {
-                            if (provider.requiresCell()) {
-                                provider.updateCellState(cs);
-                            }
-                        }
-                    }
-                } catch (RuntimeException e) {
-                    Log.e(TAG, "Exception in PhoneStateListener.onCellLocationChanged:", e);
-                }
-            }
-        }
-    }
-    
-    CellLocationUpdater mCellLocationUpdater = null;
-    CellState mLastCellState = null;
-    int mLastSignalStrength = -1;
-    int mLastRadioType = -1;
-    
-    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-
-        @Override
-        public void onCellLocationChanged(CellLocation cellLocation) {
-            synchronized (mLocationListeners) {
-                if (mCellLocationUpdater == null) {
-                    mCellLocationUpdater = new CellLocationUpdater();
-                    mCellLocationUpdater.start();
-                }
-                mCellLocationUpdater.mNextLocation = cellLocation;
-            }
-        }
-
-        @Override
-        public void onSignalStrengthChanged(int asu) {
-            synchronized (mLocationListeners) {
-                mLastSignalStrength = asu;
-    
-                if (mLastCellState != null) {
-                    mLastCellState.updateSignalStrength(asu);
-                }
-            }
-        }
-
-        @Override
-        public void onDataConnectionStateChanged(int state) {
-            synchronized (mLocationListeners) {
-                // Get radio type
-                int radioType = mTelephonyManager.getNetworkType();
-                if (radioType == TelephonyManager.NETWORK_TYPE_GPRS ||
-                    radioType == TelephonyManager.NETWORK_TYPE_EDGE) {
-                    radioType = CellState.RADIO_TYPE_GPRS;
-                } else if (radioType == TelephonyManager.NETWORK_TYPE_UMTS) {
-                    radioType = CellState.RADIO_TYPE_WCDMA;
-                }
-                mLastRadioType = radioType;
-
-                if (mLastCellState != null) {
-                    mLastCellState.updateRadioType(radioType);
-                }
-            }
-        }
-    };
-
-    private class PowerStateBroadcastReceiver extends BroadcastReceiver {
-        @Override public void onReceive(Context context, Intent intent) {
+        public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
-            if (action.equals(ALARM_INTENT)) {
-                synchronized (mLocationListeners) {
-                    log("PowerStateBroadcastReceiver: Alarm received");
-                    mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
-                    // Have to do this immediately, rather than posting a
-                    // message, so we execute our code while the system
-                    // is holding a wake lock until the alarm broadcast
-                    // is finished.
-                    acquireWakeLockLocked();
-                }
-
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                log("PowerStateBroadcastReceiver: Screen off");
-                synchronized (mLocationListeners) {
-                    updateWakelockStatusLocked(false);
-                }
-
-            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                log("PowerStateBroadcastReceiver: Screen on");
-                synchronized (mLocationListeners) {
-                    updateWakelockStatusLocked(true);
-                }
-
-            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                log("PowerStateBroadcastReceiver: Battery changed");
-                synchronized (mLocationListeners) {
-                    int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
-                    int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
-                    boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
-    
-                    // Notify collector battery state
-                    if (mCollector != null) {
-                        mCollector.updateBatteryState(scale, level, plugged);
-                    }
-                }
-            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
-                synchronized (mLocationListeners) {
+                synchronized (mLock) {
                     int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                     if (uid >= 0) {
                         ArrayList<Receiver> removedRecs = null;
@@ -1984,32 +1692,6 @@
                         }
                     }
                 }
-            }
-        }
-    }
-
-    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
-        @Override public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-
-            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
-
-                List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
-
-                if (wifiScanResults == null) {
-                    return;
-                }
-
-                // Notify provider and collector of Wifi scan results
-                synchronized (mLocationListeners) {
-                    if (mCollector != null) {
-                        mCollector.updateWifiScanResults(wifiScanResults);
-                    }
-                    if (mNetworkLocationInterface != null) {
-                        mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
-                    }
-                }
-
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 boolean noConnectivity =
                     intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
@@ -2020,484 +1702,92 @@
                 }
 
                 // Notify location providers of current network state
-                synchronized (mLocationListeners) {
-                    List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
-                    for (LocationProviderImpl provider : providers) {
+                synchronized (mLock) {
+                    for (int i = mProviders.size() - 1; i >= 0; i--) {
+                        LocationProviderProxy provider = mProviders.get(i);
                         if (provider.requiresNetwork()) {
                             provider.updateNetworkState(mNetworkState);
                         }
                     }
                 }
-
-            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                    WifiManager.WIFI_STATE_UNKNOWN);
-
-                if (state == WifiManager.WIFI_STATE_ENABLED) {
-                    mWifiEnabled = true;
-                } else if (state == WifiManager.WIFI_STATE_DISABLED) {
-                    mWifiEnabled = false;
-                } else {
-                    return;
-                }
-
-                // Notify network provider of current wifi enabled state
-                synchronized (mLocationListeners) {
-                    if (mNetworkLocationInterface != null) {
-                        mNetworkLocationInterface.updateWifiEnabledState(mWifiEnabled);
-                    }
-                }
-
-            } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
-
-                final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
-                    false);
-
-                synchronized (mLocationListeners) {
-                    if (enabled) {
-                        updateReportedGpsLocked();
-                        mGpsNavigating = true;
-                    } else {
-                        reportStopGpsLocked();
-                        mGpsNavigating = false;
-                        // When GPS is disabled, we are OK to release wake-lock
-                        mWakeLockGpsReceived = true;
-                    }
-                }
             }
-
         }
-    }
+    };
 
     // Wake locks
 
-    private void updateWakelockStatusLocked(boolean screenOn) {
-        log("updateWakelockStatus(): " + screenOn);
-
-        long callerId = Binder.clearCallingIdentity();
-        
-        boolean needsLock = false;
-        long minTime = Integer.MAX_VALUE;
-
-        if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
-            needsLock = true;
-            minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
-        }
-
-        if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
-            needsLock = true;
-            minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
-            if (screenOn) {
-                startGpsLocked();
-            } else if (mScreenOn && !screenOn) {
-                // We just turned the screen off so stop navigating
-                stopGpsLocked();
-            }
-        }
-
-        mScreenOn = screenOn;
-
-        PendingIntent sender =
-            PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
-
-        // Cancel existing alarm
-        log("Cancelling existing alarm");
-        mAlarmManager.cancel(sender);
-
-        if (needsLock && !mScreenOn) {
-            long now = SystemClock.elapsedRealtime();
-            mAlarmManager.set(
-                AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
-            mAlarmInterval = minTime;
-            log("Creating a new wakelock alarm with minTime = " + minTime);
-        } else {
-            log("No need for alarm");
-            mAlarmInterval = -1;
-
-            // Clear out existing wakelocks
-            mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
-            mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
-            releaseWakeLockLocked();
-        }
-        Binder.restoreCallingIdentity(callerId);
-    }
-
-    private void acquireWakeLockLocked() {
-        try {
-            acquireWakeLockXLocked();
-        } catch (Exception e) {
-            // This is to catch a runtime exception thrown when we try to release an
-            // already released lock.
-            Log.e(TAG, "exception in acquireWakeLock()", e);
-        }
-    }
-
-    private void acquireWakeLockXLocked() {
-        if (mWakeLock.isHeld()) {
-            log("Must release wakelock before acquiring");
-            mWakeLockAcquireTime = 0;
-            mWakeLock.release();
-        }
-
-        boolean networkActive = (mNetworkLocationProvider != null)
-                && mNetworkLocationProvider.isLocationTracking();
-        boolean gpsActive = (mGpsLocationProvider != null)
-                && mGpsLocationProvider.isLocationTracking();
-
-        boolean needsLock = networkActive || gpsActive;
-        if (!needsLock) {
-            log("No need for Lock!");
-            return;
-        }
-
-        mWakeLockGpsReceived = !gpsActive;
-        mWakeLockNetworkReceived = !networkActive;
-
-        // Acquire wake lock
-        mWakeLock.acquire();
-        mWakeLockAcquireTime = SystemClock.elapsedRealtime();
-        log("Acquired wakelock");
-
-        // Start the gps provider
-        startGpsLocked();
-
-        // Acquire cell lock
-        if (mCellWakeLockAcquired) {
-            // Lock is already acquired
-        } else if (!mWakeLockNetworkReceived) {
-            mTelephonyManager.enableLocationUpdates();
-            mCellWakeLockAcquired = true;
-        } else {
-            mCellWakeLockAcquired = false;
-        }
-
-        // Notify NetworkLocationProvider
-        if (mNetworkLocationInterface != null) {
-            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
-        }
-
-        // Acquire wifi lock
-        WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
-        if (wifiLock != null) {
-            if (mWifiWakeLockAcquired) {
-                // Lock is already acquired
-            } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
-                wifiLock.acquire();
-                mWifiWakeLockAcquired = true;
-            } else {
-                mWifiWakeLockAcquired = false;
-                Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
-            }
-        }
-    }
-
-    private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) {
-        int seq = mReportedGpsUids.get(uid, -1);
-        if (seq == curSeq) {
-            // Already reported; propagate to next sequence.
-            mReportedGpsUids.put(uid, nextSeq);
-            return true;
-        } else if (seq != nextSeq) {
-            try {
-                // New UID; report it.
-                mBatteryStats.noteStartGps(uid);
-                mReportedGpsUids.put(uid, nextSeq);
-                return true;
-            } catch (RemoteException e) {
-            }
-        }
-        return false;
-    }
-    
-    private void updateReportedGpsLocked() {
-        if (mGpsLocationProvider == null) {
-            return;
-        }
-        
-        final String name = mGpsLocationProvider.getName();
-        final int curSeq = mReportedGpsSeq;
-        final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0;
-        mReportedGpsSeq = nextSeq;
-        
-        ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name);
-        int num = 0;
-        final int N = urs.size();
-        for (int i=0; i<N; i++) {
-            UpdateRecord ur = urs.get(i);
-            if (ur.mReceiver == mProximityListener) {
-                // We don't want the system to take the blame for this one.
-                continue;
-            }
-            if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) {
-                num++;
-            }
-        }
-        
-        for (ProximityAlert pe : mProximityAlerts.values()) {
-            if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) {
-                num++;
-            }
-        }
-        
-        if (num != mReportedGpsUids.size()) {
-            // The number of uids is processed is different than the
-            // array; report any that are no longer active.
-            for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
-                if (mReportedGpsUids.valueAt(i) != nextSeq) {
-                    try {
-                        mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
-                    } catch (RemoteException e) {
-                    }
-                    mReportedGpsUids.removeAt(i);
-                }
-            }
-        }
-    }
-    
-    private void reportStopGpsLocked() {
-        int curSeq = mReportedGpsSeq;
-        for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
-            if (mReportedGpsUids.valueAt(i) == curSeq) {
+    private void incrementPendingBroadcasts() {
+        synchronized (mWakeLock) {
+            if (mPendingBroadcasts++ == 0) {
                 try {
-                    mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
-                } catch (RemoteException e) {
+                    mWakeLock.acquire();
+                    log("Acquired wakelock");
+                } catch (Exception e) {
+                    // This is to catch a runtime exception thrown when we try to release an
+                    // already released lock.
+                    Log.e(TAG, "exception in acquireWakeLock()", e);
                 }
             }
         }
-        curSeq++;
-        if (curSeq < 0) curSeq = 0;
-        mReportedGpsSeq = curSeq;
-        mReportedGpsUids.clear();
-    }
-    
-    private void startGpsLocked() {
-        boolean gpsActive = (mGpsLocationProvider != null)
-                    && mGpsLocationProvider.isLocationTracking();
-        if (gpsActive) {
-            mGpsLocationProvider.startNavigating();
-        }
     }
 
-    private void stopGpsLocked() {
-        boolean gpsActive = mGpsLocationProvider != null
-                    && mGpsLocationProvider.isLocationTracking();
-        if (gpsActive) {
-            mGpsLocationProvider.stopNavigating();
-        }
-    }
-
-    private void releaseWakeLockLocked() {
-        try {
-            releaseWakeLockXLocked();
-        } catch (Exception e) {
-            // This is to catch a runtime exception thrown when we try to release an
-            // already released lock.
-            Log.e(TAG, "exception in releaseWakeLock()", e);
-        }
-    }
-
-    private void releaseWakeLockXLocked() {
-        // Release wifi lock
-        WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
-        if (wifiLock != null) {
-            if (mWifiWakeLockAcquired) {
-                wifiLock.release();
-                mWifiWakeLockAcquired = false;
+    private void decrementPendingBroadcasts() {
+        synchronized (mWakeLock) {
+            if (--mPendingBroadcasts == 0) {
+                try {
+                    // Release wake lock
+                    if (mWakeLock.isHeld()) {
+                        mWakeLock.release();
+                        log("Released wakelock");
+                    } else {
+                        log("Can't release wakelock again!");
+                    }
+                } catch (Exception e) {
+                    // This is to catch a runtime exception thrown when we try to release an
+                    // already released lock.
+                    Log.e(TAG, "exception in releaseWakeLock()", e);
+                }
             }
         }
-
-        if (!mScreenOn) {
-            // Stop the gps
-            stopGpsLocked();
-        }
-
-        // Release cell lock
-        if (mCellWakeLockAcquired) {
-            mTelephonyManager.disableLocationUpdates();
-            mCellWakeLockAcquired = false;
-        }
-
-        // Notify NetworkLocationProvider
-        if (mNetworkLocationInterface != null) {
-            mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
-        }
-
-        // Release wake lock
-        mWakeLockAcquireTime = 0;
-        if (mWakeLock.isHeld()) {
-            log("Released wakelock");
-            mWakeLock.release();
-        } else {
-            log("Can't release wakelock again!");
-        }
     }
 
     // Geocoder
 
     public String getFromLocation(double latitude, double longitude, int maxResults,
-        String language, String country, String variant, String appName, List<Address> addrs) {
-        synchronized (mLocationListeners) {
-            if (mNetworkLocationInterface != null) {
-                return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
-                        language, country, variant, appName, addrs);
-            } else {
-                return null;
+            String language, String country, String variant, String appName, List<Address> addrs) {
+        if (mGeocodeProvider != null) {
+            try {
+                return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
+                        variant, appName,  addrs);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getFromLocation failed", e);
+                mGeocodeProvider = null;
             }
         }
+        return null;
     }
 
+
     public String getFromLocationName(String locationName,
-        double lowerLeftLatitude, double lowerLeftLongitude,
-        double upperRightLatitude, double upperRightLongitude, int maxResults,
-        String language, String country, String variant, String appName, List<Address> addrs) {
-        synchronized (mLocationListeners) {
-            if (mNetworkLocationInterface != null) {
-                return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude, 
-                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
-                        language, country, variant, appName, addrs);
-            } else {
-                return null;
+            double lowerLeftLatitude, double lowerLeftLongitude,
+            double upperRightLatitude, double upperRightLongitude, int maxResults,
+            String language, String country, String variant, String appName, List<Address> addrs) {
+
+        if (mGeocodeProvider != null) {
+            try {
+                return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
+                        lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+                        maxResults, language, country, variant, appName, addrs);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getFromLocationName failed", e);
+                mGeocodeProvider = null;
             }
         }
+        return null;
     }
 
     // Mock Providers
 
-    class MockProvider extends LocationProviderImpl {
-        boolean mRequiresNetwork;
-        boolean mRequiresSatellite;
-        boolean mRequiresCell;
-        boolean mHasMonetaryCost;
-        boolean mSupportsAltitude;
-        boolean mSupportsSpeed;
-        boolean mSupportsBearing;
-        int mPowerRequirement;
-        int mAccuracy;
-
-        public MockProvider(String name,  boolean requiresNetwork, boolean requiresSatellite,
-            boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
-            boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
-            super(name);
-
-            mRequiresNetwork = requiresNetwork;
-            mRequiresSatellite = requiresSatellite;
-            mRequiresCell = requiresCell;
-            mHasMonetaryCost = hasMonetaryCost;
-            mSupportsAltitude = supportsAltitude;
-            mSupportsBearing = supportsBearing;
-            mSupportsSpeed = supportsSpeed;
-            mPowerRequirement = powerRequirement;
-            mAccuracy = accuracy;
-        }
-
-        @Override
-        public void disable() {
-            String name = getName();
-            // We shouldn't normally need to lock, since this should only be called
-            // by the service with the lock held, but let's be paranid.
-            synchronized (mLocationListeners) {
-                mEnabledProviders.remove(name);
-                mDisabledProviders.add(name);
-            }
-        }
-
-        @Override
-        public void enable() {
-            String name = getName();
-            // We shouldn't normally need to lock, since this should only be called
-            // by the service with the lock held, but let's be paranid.
-            synchronized (mLocationListeners) {
-                mEnabledProviders.add(name);
-                mDisabledProviders.remove(name);
-            }
-        }
-
-        @Override
-        public boolean getLocation(Location l) {
-            // We shouldn't normally need to lock, since this should only be called
-            // by the service with the lock held, but let's be paranid.
-            synchronized (mLocationListeners) {
-                Location loc = mMockProviderLocation.get(getName());
-                if (loc == null) {
-                    return false;
-                }
-                l.set(loc);
-                return true;
-            }
-        }
-
-        @Override
-        public int getStatus(Bundle extras) {
-            // We shouldn't normally need to lock, since this should only be called
-            // by the service with the lock held, but let's be paranid.
-            synchronized (mLocationListeners) {
-                String name = getName();
-                Integer s = mMockProviderStatus.get(name);
-                int status = (s == null) ? AVAILABLE : s.intValue();
-                Bundle newExtras = mMockProviderStatusExtras.get(name);
-                if (newExtras != null) {
-                    extras.clear();
-                    extras.putAll(newExtras);
-                }
-                return status;
-            }
-        }
-
-        @Override
-        public boolean isEnabled() {
-            // We shouldn't normally need to lock, since this should only be called
-            // by the service with the lock held, but let's be paranid.
-            synchronized (mLocationListeners) {
-                return mEnabledProviders.contains(getName());
-            }
-        }
-
-        @Override
-        public int getAccuracy() {
-            return mAccuracy;
-        }
-
-        @Override
-        public int getPowerRequirement() {
-            return mPowerRequirement;
-        }
-
-        @Override
-        public boolean hasMonetaryCost() {
-            return mHasMonetaryCost;
-        }
-
-        @Override
-        public boolean requiresCell() {
-            return mRequiresCell;
-        }
-
-        @Override
-        public boolean requiresNetwork() {
-            return mRequiresNetwork;
-        }
-
-        @Override
-        public boolean requiresSatellite() {
-            return mRequiresSatellite;
-        }
-
-        @Override
-        public boolean supportsAltitude() {
-            return mSupportsAltitude;
-        }
-
-        @Override
-        public boolean supportsBearing() {
-            return mSupportsBearing;
-        }
-
-        @Override
-        public boolean supportsSpeed() {
-            return mSupportsSpeed;
-        }
-    }
-    
     private void checkMockPermissionsSafe() {
         boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
@@ -2516,60 +1806,75 @@
         boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
         checkMockPermissionsSafe();
 
-        synchronized (mLocationListeners) {
-            MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
+        synchronized (mLock) {
+            MockProvider provider = new MockProvider(name, this,
+                requiresNetwork, requiresSatellite,
                 requiresCell, hasMonetaryCost, supportsAltitude,
                 supportsSpeed, supportsBearing, powerRequirement, accuracy);
-            if (LocationProviderImpl.getProvider(name) != null) {
+            if (mProvidersByName.get(name) != null) {
                 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
             }
-            LocationProviderImpl.addProvider(provider);
+
+            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
+            long identity = Binder.clearCallingIdentity();
+            addProvider(new LocationProviderProxy(name, provider));
+            mMockProviders.put(name, provider);
             updateProvidersLocked();
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
     public void removeTestProvider(String provider) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
-            if (p == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            LocationProviderImpl.removeProvider(p);
+            removeProvider(mProvidersByName.get(provider));
+            mMockProviders.remove(mockProvider);
             updateProvidersLocked();
         }
     }
 
     public void setTestProviderLocation(String provider, Location loc) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            if (LocationProviderImpl.getProvider(provider) == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            mMockProviderLocation.put(provider, loc);
+            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
+            long identity = Binder.clearCallingIdentity();
+            mockProvider.setLocation(loc);
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
     public void clearTestProviderLocation(String provider) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            if (LocationProviderImpl.getProvider(provider) == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            mMockProviderLocation.remove(provider);
+            mockProvider.clearLocation();
         }
     }
 
     public void setTestProviderEnabled(String provider, boolean enabled) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            if (LocationProviderImpl.getProvider(provider) == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             if (enabled) {
+                mockProvider.enable();
                 mEnabledProviders.add(provider);
                 mDisabledProviders.remove(provider);
             } else {
+                mockProvider.disable();
                 mEnabledProviders.remove(provider);
                 mDisabledProviders.add(provider);
             }
@@ -2579,8 +1884,9 @@
 
     public void clearTestProviderEnabled(String provider) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            if (LocationProviderImpl.getProvider(provider) == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             mEnabledProviders.remove(provider);
@@ -2591,25 +1897,23 @@
 
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            if (LocationProviderImpl.getProvider(provider) == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            mMockProviderStatus.put(provider, new Integer(status));
-            mMockProviderStatusExtras.put(provider, extras);
-            mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
+            mockProvider.setStatus(status, extras, updateTime);
         }
     }
 
     public void clearTestProviderStatus(String provider) {
         checkMockPermissionsSafe();
-        synchronized (mLocationListeners) {
-            if (LocationProviderImpl.getProvider(provider) == null) {
+        synchronized (mLock) {
+            MockProvider mockProvider = mMockProviders.get(provider);
+            if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            mMockProviderStatus.remove(provider);
-            mMockProviderStatusExtras.remove(provider);
-            mMockProviderStatusUpdateTime.remove(provider);
+            mockProvider.clearStatus();
         }
     }
 
@@ -2622,63 +1926,29 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump AlarmManager from from pid="
+            pw.println("Permission Denial: can't dump LocationManagerService from from pid="
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid());
             return;
         }
         
-        synchronized (mLocationListeners) {
+        synchronized (mLock) {
             pw.println("Current Location Manager state:");
             pw.println("  sProvidersLoaded=" + sProvidersLoaded);
-            pw.println("  mGpsLocationProvider=" + mGpsLocationProvider);
-            pw.println("  mGpsNavigating=" + mGpsNavigating);
-            pw.println("  mNetworkLocationProvider=" + mNetworkLocationProvider);
-            pw.println("  mNetworkLocationInterface=" + mNetworkLocationInterface);
-            pw.println("  mLastSignalStrength=" + mLastSignalStrength
-                    + "  mLastRadioType=" + mLastRadioType);
-            pw.println("  mCellLocationUpdater=" + mCellLocationUpdater);
-            pw.println("  mLastCellState=" + mLastCellState);
             pw.println("  mCollector=" + mCollector);
-            pw.println("  mAlarmInterval=" + mAlarmInterval
-                    + " mScreenOn=" + mScreenOn
-                    + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
-            pw.println("  mWakeLockGpsReceived=" + mWakeLockGpsReceived
-                    + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
-            pw.println("  mWifiWakeLockAcquired=" + mWifiWakeLockAcquired
-                    + " mCellWakeLockAcquired=" + mCellWakeLockAcquired);
             pw.println("  Listeners:");
-            int N = mListeners.size();
+            int N = mReceivers.size();
             for (int i=0; i<N; i++) {
-                pw.println("    " + mListeners.get(i));
+                pw.println("    " + mReceivers.get(i));
             }
             pw.println("  Location Listeners:");
-            for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i
-                    : mLocationListeners.entrySet()) {
-                pw.println("    " + i.getKey() + ":");
-                for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) {
+            for (Receiver i : mReceivers.values()) {
+                pw.println("    " + i + ":");
+                for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
                     pw.println("      " + j.getKey() + ":");
                     j.getValue().dump(pw, "        ");
                 }
             }
-            pw.println("  Last Fix Broadcasts:");
-            for (Map.Entry<Receiver, HashMap<String,Location>> i
-                    : mLastFixBroadcast.entrySet()) {
-                pw.println("    " + i.getKey() + ":");
-                for (Map.Entry<String,Location> j : i.getValue().entrySet()) {
-                    pw.println("      " + j.getKey() + ":");
-                    j.getValue().dump(new PrintWriterPrinter(pw), "        ");
-                }
-            }
-            pw.println("  Last Status Broadcasts:");
-            for (Map.Entry<Receiver, HashMap<String,Long>> i
-                    : mLastStatusBroadcast.entrySet()) {
-                pw.println("    " + i.getKey() + ":");
-                for (Map.Entry<String,Long> j : i.getValue().entrySet()) {
-                    pw.println("      " + j.getKey() + " -> 0x"
-                            + Long.toHexString(j.getValue()));
-                }
-            }
             pw.println("  Records by Provider:");
             for (Map.Entry<String, ArrayList<UpdateRecord>> i
                     : mRecordsByProvider.entrySet()) {
@@ -2688,12 +1958,6 @@
                     j.dump(pw, "        ");
                 }
             }
-            pw.println("  Locations by Provider:");
-            for (Map.Entry<String, Location> i
-                    : mLocationsByProvider.entrySet()) {
-                pw.println("    " + i.getKey() + ":");
-                i.getValue().dump(new PrintWriterPrinter(pw), "      ");
-            }
             pw.println("  Last Known Locations:");
             for (Map.Entry<String, Location> i
                     : mLastKnownLocation.entrySet()) {
@@ -2715,6 +1979,7 @@
                     i.dump(pw, "      ");
                 }
             }
+            pw.println("  mProximityReceiver=" + mProximityReceiver);
             pw.println("  mProximityListener=" + mProximityListener);
             if (mEnabledProviders.size() > 0) {
                 pw.println("  Enabled Providers:");
@@ -2733,42 +1998,9 @@
             if (mMockProviders.size() > 0) {
                 pw.println("  Mock Providers:");
                 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
-                    pw.println("    " + i.getKey() + " -> " + i.getValue());
+                    i.getValue().dump(pw, "      ");
                 }
             }
-            if (mMockProviderLocation.size() > 0) {
-                pw.println("  Mock Provider Location:");
-                for (Map.Entry<String, Location> i : mMockProviderLocation.entrySet()) {
-                    pw.println("    " + i.getKey() + ":");
-                    i.getValue().dump(new PrintWriterPrinter(pw), "      ");
-                }
-            }
-            if (mMockProviderStatus.size() > 0) {
-                pw.println("  Mock Provider Status:");
-                for (Map.Entry<String, Integer> i : mMockProviderStatus.entrySet()) {
-                    pw.println("    " + i.getKey() + " -> 0x"
-                            + Integer.toHexString(i.getValue()));
-                }
-            }
-            if (mMockProviderStatusExtras.size() > 0) {
-                pw.println("  Mock Provider Status Extras:");
-                for (Map.Entry<String, Bundle> i : mMockProviderStatusExtras.entrySet()) {
-                    pw.println("    " + i.getKey() + " -> " + i.getValue());
-                }
-            }
-            if (mMockProviderStatusUpdateTime.size() > 0) {
-                pw.println("  Mock Provider Status Update Time:");
-                for (Map.Entry<String, Long> i : mMockProviderStatusUpdateTime.entrySet()) {
-                    pw.println("    " + i.getKey() + " -> " + i.getValue());
-                }
-            }
-            pw.println("  Reported GPS UIDs @ seq " + mReportedGpsSeq + ":");
-            N = mReportedGpsUids.size();
-            for (int i=0; i<N; i++)  {
-                pw.println("    UID " + mReportedGpsUids.keyAt(i)
-                        + " seq=" + mReportedGpsUids.valueAt(i));
-            }
         }
     }
 }
-
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 9e062f4..4ee0c91 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -59,6 +59,7 @@
 import android.content.pm.Signature;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Parcel;
@@ -140,8 +141,9 @@
             Process.THREAD_PRIORITY_BACKGROUND);
     final Handler mHandler;
 
-    final int mSdkVersion = SystemProperties.getInt(
-            "ro.build.version.sdk", 0);
+    final int mSdkVersion = Build.VERSION.SDK_INT;
+    final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME)
+            ? null : Build.VERSION.CODENAME;
     
     final Context mContext;
     final boolean mFactoryTest;
@@ -1751,7 +1753,7 @@
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser(scanFile.getPath());
         pp.setSeparateProcesses(mSeparateProcesses);
-        pp.setSdkVersion(mSdkVersion);
+        pp.setSdkVersion(mSdkVersion, mSdkCodename);
         final PackageParser.Package pkg = pp.parsePackage(scanFile,
                 destCodeFile.getAbsolutePath(), mMetrics, parseFlags);
         if (pkg == null) {
@@ -1761,8 +1763,7 @@
         PackageSetting ps;
         PackageSetting updatedPkg;
         synchronized (mPackages) {
-            ps = mSettings.peekPackageLP(pkg.packageName,
-                    scanFile.toString());
+            ps = mSettings.peekPackageLP(pkg.packageName);
             updatedPkg = mSettings.mDisabledSysPackages.get(pkg.packageName);
         }
         if (updatedPkg != null) {
@@ -1771,13 +1772,21 @@
         }
         if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
             // Check for updated system applications here
-            if ((updatedPkg != null) && (ps == null)) {
-                // The system package has been updated and the code path does not match
-                // Ignore entry. Just return
-                Log.w(TAG, "Package:" + pkg.packageName + 
-                        " has been updated. Ignoring the one from path:"+scanFile);
-                mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-                return null;
+            if (updatedPkg != null) {
+                if ((ps != null) && (!ps.codePath.getPath().equals(scanFile.getPath()))) {
+                    if (pkg.mVersionCode <= ps.versionCode) {
+                     // The system package has been updated and the code path does not match
+                        // Ignore entry. Just return
+                        Log.w(TAG, "Package:" + pkg.packageName +
+                                " has been updated. Ignoring the one from path:"+scanFile);
+                        mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
+                        return null;
+                    } else {
+                        // Delete the older apk pointed to by ps
+                        deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString);
+                        mSettings.enableSystemPackageLP(ps.name);
+                    }
+                }
             }
         }
         if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {
@@ -2932,11 +2941,12 @@
         }
 
         @Override
-        protected void dumpFilter(Printer out, String prefix,
+        protected void dumpFilter(PrintWriter out, String prefix,
                 PackageParser.ActivityIntentInfo filter) {
-            out.println(prefix
-                    + Integer.toHexString(System.identityHashCode(filter.activity))
-                    + " " + filter.activity.component.flattenToShortString());
+            out.print(prefix); out.print(
+                    Integer.toHexString(System.identityHashCode(filter.activity)));
+                    out.print(' ');
+                    out.println(filter.activity.componentShortName);
         }
 
 //        List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
@@ -3064,11 +3074,12 @@
         }
 
         @Override
-        protected void dumpFilter(Printer out, String prefix,
+        protected void dumpFilter(PrintWriter out, String prefix,
                 PackageParser.ServiceIntentInfo filter) {
-            out.println(prefix
-                    + Integer.toHexString(System.identityHashCode(filter.service))
-                    + " " + filter.service.component.flattenToShortString());
+            out.print(prefix); out.print(
+                    Integer.toHexString(System.identityHashCode(filter.service)));
+                    out.print(' ');
+                    out.println(filter.service.componentShortName);
         }
 
 //        List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
@@ -3220,20 +3231,27 @@
         private final String mRootDir;
         private final boolean mIsRom;
     }
-    
+
     /* Called when a downloaded package installation has been confirmed by the user */
     public void installPackage(
             final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
+        installPackage(packageURI, observer, flags, null);
+    }
+    
+    /* Called when a downloaded package installation has been confirmed by the user */
+    public void installPackage(
+            final Uri packageURI, final IPackageInstallObserver observer, final int flags,
+            final String installerPackageName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INSTALL_PACKAGES, null);
-
+        
         // Queue up an async operation since the package installation may take a little while.
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
                 PackageInstalledInfo res;
                 synchronized (mInstallLock) {
-                    res = installPackageLI(packageURI, flags, true);
+                    res = installPackageLI(packageURI, flags, true, installerPackageName);
                 }
                 if (observer != null) {
                     try {
@@ -3281,7 +3299,7 @@
             File tmpPackageFile, 
             String destFilePath, File destPackageFile, File destResourceFile,
             PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
-            PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res) {
         // Remember this for later, in case we need to rollback this install
         boolean dataDirExists = (new File(mAppDataDir, pkgName)).exists();
         res.name = pkgName;
@@ -3317,7 +3335,8 @@
                     destResourceFile, pkg, 
                     newPackage,
                     true,
-                    forwardLocked,  
+                    forwardLocked,
+                    installerPackageName,
                     res);
             // delete the partially installed application. the data directory will have to be
             // restored if it was already existing
@@ -3338,26 +3357,27 @@
             File tmpPackageFile, 
             String destFilePath, File destPackageFile, File destResourceFile,
             PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
-            PackageInstalledInfo res) {
-        PackageParser.Package deletedPackage;
+            String installerPackageName, PackageInstalledInfo res) {
+
+        PackageParser.Package oldPackage;
         // First find the old package info and check signatures
         synchronized(mPackages) {
-            deletedPackage = mPackages.get(pkgName);
-            if(checkSignaturesLP(pkg, deletedPackage) != PackageManager.SIGNATURE_MATCH) {
+            oldPackage = mPackages.get(pkgName);
+            if(checkSignaturesLP(pkg, oldPackage) != PackageManager.SIGNATURE_MATCH) {
                 res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
                 return;
             }
         }
-        boolean sysPkg = ((deletedPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+        boolean sysPkg = ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
         if(sysPkg) {
-            replaceSystemPackageLI(deletedPackage, 
+            replaceSystemPackageLI(oldPackage,
                     tmpPackageFile, destFilePath, 
                     destPackageFile, destResourceFile, pkg, forwardLocked,
-                    newInstall, res);
+                    newInstall, installerPackageName, res);
         } else {
-            replaceNonSystemPackageLI(deletedPackage, tmpPackageFile, destFilePath, 
+            replaceNonSystemPackageLI(oldPackage, tmpPackageFile, destFilePath,
                     destPackageFile, destResourceFile, pkg, forwardLocked,
-                    newInstall, res);
+                    newInstall, installerPackageName, res);
         }
     }
     
@@ -3365,12 +3385,18 @@
             File tmpPackageFile, 
             String destFilePath, File destPackageFile, File destResourceFile,
             PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
-            PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         String pkgName = deletedPackage.packageName;
         boolean deletedPkg = true;
         boolean updatedSettings = false;
-        int parseFlags = PackageManager.REPLACE_EXISTING_PACKAGE;
+        
+        String oldInstallerPackageName = null;
+        synchronized (mPackages) {
+            oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName);
+        }
+        
+        int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING;
         // First delete the existing package while retaining the data directory
         if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
                 res.removedInfo)) {
@@ -3398,6 +3424,7 @@
                         newPackage,
                         true,
                         forwardLocked,  
+                        installerPackageName,
                         res);
                 updatedSettings = true;
             }
@@ -3441,8 +3468,8 @@
                 installPackageLI(
                         Uri.fromFile(new File(deletedPackage.mPath)),
                         isForwardLocked(deletedPackage)
-                        ? PackageManager.FORWARD_LOCK_PACKAGE
-                                : 0, false);
+                        ? PackageManager.INSTALL_FORWARD_LOCK
+                                : 0, false, oldInstallerPackageName);
             }
         }
     }
@@ -3451,10 +3478,10 @@
             File tmpPackageFile, 
             String destFilePath, File destPackageFile, File destResourceFile,
             PackageParser.Package pkg, boolean forwardLocked, boolean newInstall,
-            PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         boolean updatedSettings = false;
-        int parseFlags = PackageManager.REPLACE_EXISTING_PACKAGE |
+        int parseFlags = PackageManager.INSTALL_REPLACE_EXISTING |
                 PackageParser.PARSE_IS_SYSTEM;
         String packageName = deletedPackage.packageName;
         res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -3501,7 +3528,8 @@
                     destResourceFile, pkg, 
                     newPackage,
                     true,
-                    forwardLocked,  
+                    forwardLocked,
+                    installerPackageName,
                     res);
             updatedSettings = true;
         }
@@ -3528,6 +3556,8 @@
             synchronized(mPackages) {
                 if(updatedSettings) {
                     mSettings.enableSystemPackageLP(packageName);
+                    mSettings.setInstallerPackageName(packageName,
+                            oldPkgSetting.installerPackageName);
                 }
                 mSettings.writeLP();
             }
@@ -3541,7 +3571,7 @@
             PackageParser.Package newPackage,
             boolean replacingExistingPackage,
             boolean forwardLocked,  
-            PackageInstalledInfo res) {
+            String installerPackageName, PackageInstalledInfo res) {
         synchronized (mPackages) {
             //write settings. the installStatus will be incomplete at this stage.
             //note that the new package setting would have already been
@@ -3588,6 +3618,7 @@
             res.uid = newPackage.applicationInfo.uid;
             res.pkg = newPackage;
             mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE);
+            mSettings.setInstallerPackageName(pkgName, installerPackageName);
             res.returnCode = PackageManager.INSTALL_SUCCEEDED;
             //to update install status
             mSettings.writeLP();
@@ -3595,7 +3626,7 @@
     }
     
     private PackageInstalledInfo installPackageLI(Uri pPackageURI,
-            int pFlags, boolean newInstall) {
+            int pFlags, boolean newInstall, String installerPackageName) {
         File tmpPackageFile = null;
         String pkgName = null;
         boolean forwardLocked = false;
@@ -3666,13 +3697,13 @@
             res.name = pkgName;
             //initialize some variables before installing pkg
             final String pkgFileName = pkgName + ".apk";
-            final File destDir = ((pFlags&PackageManager.FORWARD_LOCK_PACKAGE) != 0)
+            final File destDir = ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0)
                                  ?  mDrmAppPrivateInstallDir
                                  : mAppInstallDir;
             final File destPackageFile = new File(destDir, pkgFileName);
             final String destFilePath = destPackageFile.getAbsolutePath();
             File destResourceFile;
-            if ((pFlags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) {
+            if ((pFlags&PackageManager.INSTALL_FORWARD_LOCK) != 0) {
                 final String publicZipFileName = pkgName + ".zip";
                 destResourceFile = new File(mAppInstallDir, publicZipFileName);
                 forwardLocked = true;
@@ -3684,13 +3715,19 @@
             parseFlags |= mDefParseFlags;
             PackageParser pp = new PackageParser(tmpPackageFile.getPath());
             pp.setSeparateProcesses(mSeparateProcesses);
-            pp.setSdkVersion(mSdkVersion);
+            pp.setSdkVersion(mSdkVersion, mSdkCodename);
             final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
                     destPackageFile.getAbsolutePath(), mMetrics, parseFlags);
             if (pkg == null) {
                 res.returnCode = pp.getParseError();
                 break main_flow;
             }
+            if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+                if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
+                    res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
+                    break main_flow;
+                }
+            }
             if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
                 res.returnCode = pp.getParseError();
                 break main_flow;
@@ -3698,7 +3735,7 @@
             
             synchronized (mPackages) {
                 //check if installing already existing package
-                if ((pFlags&PackageManager.REPLACE_EXISTING_PACKAGE) != 0
+                if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
                         && mPackages.containsKey(pkgName)) {
                     replacingExistingPackage = true;
                 }
@@ -3708,13 +3745,13 @@
                 replacePackageLI(pkgName,
                         tmpPackageFile, 
                         destFilePath, destPackageFile, destResourceFile,
-                        pkg, forwardLocked, newInstall,
+                        pkg, forwardLocked, newInstall, installerPackageName,
                         res);
             } else {
                 installNewPackageLI(pkgName,
                         tmpPackageFile, 
                         destFilePath, destPackageFile, destResourceFile,
-                        pkg, forwardLocked, newInstall,
+                        pkg, forwardLocked, newInstall, installerPackageName,
                         res);
             }
         } finally {
@@ -3994,7 +4031,7 @@
      * Tries to delete system package.
      */
     private boolean deleteSystemPackageLI(PackageParser.Package p,
-            boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) {
+            int flags, PackageRemovedInfo outInfo) {
         ApplicationInfo applicationInfo = p.applicationInfo;
         //applicable for non-partially installed applications only
         if (applicationInfo == null) {
@@ -4016,6 +4053,19 @@
         }
         // Delete the updated package
         outInfo.isRemovedPackageSystemUpdate = true;
+        boolean deleteCodeAndResources = false;
+        if (ps.versionCode <  p.mVersionCode) {
+            // Delete code and resources for downgrades
+            deleteCodeAndResources = true;
+            if ((flags & PackageManager.DONT_DELETE_DATA) == 0) {
+                flags &= ~PackageManager.DONT_DELETE_DATA;
+            }
+        } else {
+            // Preserve data by setting flag
+            if ((flags & PackageManager.DONT_DELETE_DATA) == 0) {
+                flags |= PackageManager.DONT_DELETE_DATA;
+            }
+        }
         boolean ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo);
         if (!ret) {
             return false;
@@ -4039,6 +4089,28 @@
         return true;
     }
     
+    private void deletePackageResourcesLI(String packageName,
+            String sourceDir, String publicSourceDir) {
+        File sourceFile = new File(sourceDir);
+        if (!sourceFile.exists()) {
+            Log.w(TAG, "Package source " + sourceDir + " does not exist.");
+        }
+        // Delete application's code and resources
+        sourceFile.delete();
+        final File publicSourceFile = new File(publicSourceDir);
+        if (publicSourceFile.exists()) {
+            publicSourceFile.delete();
+        }
+        if (mInstaller != null) {
+            int retCode = mInstaller.rmdex(sourceFile.toString());
+            if (retCode < 0) {
+                Log.w(TAG, "Couldn't remove dex file for package: "
+                        + packageName + " at location " + sourceFile.toString() + ", retcode=" + retCode);
+                // we don't consider this to be a failure of the core package deletion
+            }
+        }
+    }
+    
     private boolean deleteInstalledPackageLI(PackageParser.Package p,
             boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) {
         ApplicationInfo applicationInfo = p.applicationInfo;
@@ -4046,11 +4118,6 @@
             Log.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
             return false;
         }
-        // Delete application's source directory
-        File sourceFile = new File(applicationInfo.sourceDir);
-        if (!sourceFile.exists()) {
-            Log.w(TAG, "Package source " + applicationInfo.sourceDir + " does not exist.");
-        }
         outInfo.uid = applicationInfo.uid;
 
         // Delete package data from internal structures and also remove data if flag is set
@@ -4058,19 +4125,8 @@
 
         // Delete application code and resources
         if (deleteCodeAndResources) {
-            sourceFile.delete();
-            final File publicSourceFile = new File(applicationInfo.publicSourceDir);
-            if (publicSourceFile.exists()) {
-                publicSourceFile.delete();
-            }
-            if (mInstaller != null) {
-                int retCode = mInstaller.rmdex(sourceFile.toString());
-                if (retCode < 0) {
-                    Log.w(TAG, "Couldn't remove dex file for package: "
-                            + p.packageName + " at location " + sourceFile.toString() + ", retcode=" + retCode);
-                    // we don't consider this to be a failure of the core package deletion
-                }
-            }
+            deletePackageResourcesLI(applicationInfo.packageName,
+                    applicationInfo.sourceDir, applicationInfo.publicSourceDir);
         }
         return true;
     }
@@ -4118,7 +4174,7 @@
             Log.i(TAG, "Removing system package:"+p.packageName);
             // When an updated system application is deleted we delete the existing resources as well and
             // fall back to existing code in system partition
-            return deleteSystemPackageLI(p, true, flags, outInfo);
+            return deleteSystemPackageLI(p, flags, outInfo);
         }
         Log.i(TAG, "Removing non-system package:"+p.packageName);
         return deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
@@ -4513,6 +4569,16 @@
         }
     }
 
+    public String getInstallerPackageName(String packageName) {
+        synchronized (mPackages) {
+            PackageSetting pkg = mSettings.mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            return pkg.installerPackageName;
+        }
+    }
+    
     public int getApplicationEnabledSetting(String appPackageName) {
         synchronized (mPackages) {
             PackageSetting pkg = mSettings.mPackages.get(appPackageName);
@@ -4578,80 +4644,83 @@
             return;
         }
 
-        Printer printer = new PrintWriterPrinter(pw);
         synchronized (mPackages) {
             pw.println("Activity Resolver Table:");
-            mActivities.dump(printer, "  ");
+            mActivities.dump(pw, "  ");
             pw.println(" ");
             pw.println("Receiver Resolver Table:");
-            mReceivers.dump(printer, "  ");
+            mReceivers.dump(pw, "  ");
             pw.println(" ");
             pw.println("Service Resolver Table:");
-            mServices.dump(printer, "  ");
+            mServices.dump(pw, "  ");
             pw.println(" ");
             pw.println("Preferred Activities:");
-            mSettings.mPreferredActivities.dump(printer, "  ");
+            mSettings.mPreferredActivities.dump(pw, "  ");
             pw.println(" ");
             pw.println("Preferred Packages:");
             {
                 for (PackageSetting ps : mSettings.mPreferredPackages) {
-                    pw.println("  " + ps.name);
+                    pw.print("  "); pw.println(ps.name);
                 }
             }
             pw.println(" ");
             pw.println("Permissions:");
             {
                 for (BasePermission p : mSettings.mPermissions.values()) {
-                    pw.println("  Permission [" + p.name + "] ("
-                            + Integer.toHexString(System.identityHashCode(p))
-                            + "):");
-                    pw.println("    sourcePackage=" + p.sourcePackage);
-                    pw.println("    uid=" + p.uid
-                            + " gids=" + arrayToString(p.gids)
-                            + " type=" + p.type);
+                    pw.print("  Permission ["); pw.print(p.name); pw.print("] (");
+                            pw.print(Integer.toHexString(System.identityHashCode(p)));
+                            pw.println("):");
+                    pw.print("    sourcePackage="); pw.println(p.sourcePackage);
+                    pw.print("    uid="); pw.print(p.uid);
+                            pw.print(" gids="); pw.print(arrayToString(p.gids));
+                            pw.print(" type="); pw.println(p.type);
                 }
             }
             pw.println(" ");
             pw.println("Packages:");
             {
                 for (PackageSetting ps : mSettings.mPackages.values()) {
-                    pw.println("  Package [" + ps.name + "] ("
-                            + Integer.toHexString(System.identityHashCode(ps))
-                            + "):");
-                    pw.println("    userId=" + ps.userId
-                            + " gids=" + arrayToString(ps.gids));
-                    pw.println("    sharedUser=" + ps.sharedUser);
-                    pw.println("    pkg=" + ps.pkg);
-                    pw.println("    codePath=" + ps.codePathString);
-                    pw.println("    resourcePath=" + ps.resourcePathString);
+                    pw.print("  Package ["); pw.print(ps.name); pw.print("] (");
+                            pw.print(Integer.toHexString(System.identityHashCode(ps)));
+                            pw.println("):");
+                    pw.print("    userId="); pw.print(ps.userId);
+                            pw.print(" gids="); pw.println(arrayToString(ps.gids));
+                    pw.print("    sharedUser="); pw.println(ps.sharedUser);
+                    pw.print("    pkg="); pw.println(ps.pkg);
+                    pw.print("    codePath="); pw.println(ps.codePathString);
+                    pw.print("    resourcePath="); pw.println(ps.resourcePathString);
                     if (ps.pkg != null) {
-                        pw.println("    dataDir=" + ps.pkg.applicationInfo.dataDir);
+                        pw.print("    dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
                     }
-                    pw.println("    timeStamp=" + ps.getTimeStampStr());
-                    pw.println("    signatures=" + ps.signatures);
-                    pw.println("    permissionsFixed=" + ps.permissionsFixed
-                            + " pkgFlags=0x" + Integer.toHexString(ps.pkgFlags)
-                            + " installStatus=" + ps.installStatus
-                            + " enabled=" + ps.enabled);
+                    pw.print("    timeStamp="); pw.println(ps.getTimeStampStr());
+                    pw.print("    signatures="); pw.println(ps.signatures);
+                    pw.print("    permissionsFixed="); pw.print(ps.permissionsFixed);
+                            pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
+                            pw.print(" installStatus="); pw.print(ps.installStatus);
+                            pw.print(" enabled="); pw.println(ps.enabled);
                     if (ps.disabledComponents.size() > 0) {
                         pw.println("    disabledComponents:");
                         for (String s : ps.disabledComponents) {
-                            pw.println("      " + s);
+                            pw.print("      "); pw.println(s);
                         }
                     }
                     if (ps.enabledComponents.size() > 0) {
                         pw.println("    enabledComponents:");
                         for (String s : ps.enabledComponents) {
-                            pw.println("      " + s);
+                            pw.print("      "); pw.println(s);
                         }
                     }
-                    pw.println("    grantedPermissions:");
-                    for (String s : ps.grantedPermissions) {
-                        pw.println("      " + s);
+                    if (ps.grantedPermissions.size() > 0) {
+                        pw.println("    grantedPermissions:");
+                        for (String s : ps.grantedPermissions) {
+                            pw.print("      "); pw.println(s);
+                        }
                     }
-                    pw.println("    loadedPermissions:");
-                    for (String s : ps.loadedPermissions) {
-                        pw.println("      " + s);
+                    if (ps.loadedPermissions.size() > 0) {
+                        pw.println("    loadedPermissions:");
+                        for (String s : ps.loadedPermissions) {
+                            pw.print("      "); pw.println(s);
+                        }
                     }
                 }
             }
@@ -4659,18 +4728,18 @@
             pw.println("Shared Users:");
             {
                 for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
-                    pw.println("  SharedUser [" + su.name + "] ("
-                            + Integer.toHexString(System.identityHashCode(su))
-                            + "):");
-                    pw.println("    userId=" + su.userId
-                            + " gids=" + arrayToString(su.gids));
+                    pw.print("  SharedUser ["); pw.print(su.name); pw.print("] (");
+                            pw.print(Integer.toHexString(System.identityHashCode(su)));
+                            pw.println("):");
+                    pw.print("    userId="); pw.print(su.userId);
+                            pw.print(" gids="); pw.println(arrayToString(su.gids));
                     pw.println("    grantedPermissions:");
                     for (String s : su.grantedPermissions) {
-                        pw.println("      " + s);
+                        pw.print("      "); pw.println(s);
                     }
                     pw.println("    loadedPermissions:");
                     for (String s : su.loadedPermissions) {
-                        pw.println("      " + s);
+                        pw.print("      "); pw.println(s);
                     }
                 }
             }
@@ -5147,6 +5216,7 @@
         final String resourcePathString;
         private long timeStamp;
         private String timeStampString = "0";
+        final int versionCode;
 
         PackageSignatures signatures = new PackageSignatures();
 
@@ -5158,17 +5228,29 @@
         HashSet<String> enabledComponents = new HashSet<String>(0);
         int enabled = COMPONENT_ENABLED_STATE_DEFAULT;
         int installStatus = PKG_INSTALL_COMPLETE;
+        
+        /* package name of the app that installed this package */
+        String installerPackageName;
 
         PackageSettingBase(String name, File codePath, File resourcePath,
-                int pkgFlags) {
+                int pVersionCode, int pkgFlags) {
             super(pkgFlags);
             this.name = name;
             this.codePath = codePath;
             this.codePathString = codePath.toString();
             this.resourcePath = resourcePath;
             this.resourcePathString = resourcePath.toString();
+            this.versionCode = pVersionCode;
         }
 
+        public void setInstallerPackageName(String packageName) {
+            installerPackageName = packageName;
+        }
+        
+        String getInstallerPackageName() {
+            return installerPackageName;
+        }
+        
         public void setInstallStatus(int newStatus) {
             installStatus = newStatus;
         }
@@ -5247,8 +5329,8 @@
         SharedUserSetting sharedUser;
 
         PackageSetting(String name, File codePath, File resourcePath,
-                 int pkgFlags) {
-            super(name, codePath, resourcePath, pkgFlags);
+                int pVersionCode, int pkgFlags) {
+            super(name, codePath, resourcePath, pVersionCode, pkgFlags);
         }
         
         @Override
@@ -5302,16 +5384,19 @@
         private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
                     new IntentResolver<PreferredActivity, PreferredActivity>() {
             @Override
-            protected void dumpFilter(Printer out, String prefix,
+            protected void dumpFilter(PrintWriter out, String prefix,
                     PreferredActivity filter) {
-                out.println(prefix
-                        + Integer.toHexString(System.identityHashCode(filter))
-                        + " " + filter.mActivity.flattenToShortString()
-                        + " match=0x" + Integer.toHexString(filter.mMatch));
+                out.print(prefix); out.print(
+                        Integer.toHexString(System.identityHashCode(filter)));
+                        out.print(' ');
+                        out.print(filter.mActivity.flattenToShortString());
+                        out.print(" match=0x");
+                        out.println( Integer.toHexString(filter.mMatch));
                 if (filter.mSetComponents != null) {
-                    out.println(prefix + "  Selected from:");
+                    out.print(prefix); out.println("  Selected from:");
                     for (int i=0; i<filter.mSetComponents.length; i++) {
-                        out.println(prefix + "    " + filter.mSetComponents[i]);
+                        out.print(prefix); out.print("    ");
+                                out.println(filter.mSetComponents[i]);
                     }
                 }
             }
@@ -5343,8 +5428,8 @@
             final int sharedId;
 
             PendingPackage(String name, File codePath, File resourcePath,
-                    int sharedId, int pkgFlags) {
-                super(name, codePath, resourcePath, pkgFlags);
+                    int sharedId, int pVersionCode, int pkgFlags) {
+                super(name, codePath, resourcePath, pVersionCode, pkgFlags);
                 this.sharedId = sharedId;
             }
         }
@@ -5368,7 +5453,7 @@
                 int pkgFlags, boolean create, boolean add) {
             final String name = pkg.packageName;
             PackageSetting p = getPackageLP(name, sharedUser, codePath,
-                    resourcePath, pkgFlags, create, add);
+                    resourcePath, pkg.mVersionCode, pkgFlags, create, add);
 
             if (p != null) {
                 p.pkg = pkg;
@@ -5376,12 +5461,15 @@
             return p;
         }
         
-        PackageSetting peekPackageLP(String name, String codePath) {
+        PackageSetting peekPackageLP(String name) {
+            return mPackages.get(name);
+            /*
             PackageSetting p = mPackages.get(name);
             if (p != null && p.codePath.getPath().equals(codePath)) {
                 return p;
             }
             return null;
+            */
         }
         
         void setInstallStatus(String pkgName, int status) {
@@ -5393,6 +5481,19 @@
             }
         }
         
+        void setInstallerPackageName(String pkgName,
+                String installerPkgName) {
+            PackageSetting p = mPackages.get(pkgName);
+            if(p != null) {
+                p.setInstallerPackageName(installerPkgName);
+            }
+        }
+        
+        String getInstallerPackageName(String pkgName) {
+            PackageSetting p = mPackages.get(pkgName);
+            return (p == null) ? null : p.getInstallerPackageName(); 
+        }
+
         int getInstallStatus(String pkgName) {
             PackageSetting p = mPackages.get(pkgName);
             if(p != null) {
@@ -5453,13 +5554,13 @@
                 p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
             PackageSetting ret = addPackageLP(name, p.codePath,
-                    p.resourcePath, p.userId, p.pkgFlags);
+                    p.resourcePath, p.userId, p.versionCode, p.pkgFlags);
             mDisabledSysPackages.remove(name);
             return ret;
         }
         
         PackageSetting addPackageLP(String name, File codePath,
-                File resourcePath, int uid, int pkgFlags) {
+                File resourcePath, int uid, int vc, int pkgFlags) {
             PackageSetting p = mPackages.get(name);
             if (p != null) {
                 if (p.userId == uid) {
@@ -5469,7 +5570,7 @@
                         "Adding duplicate package, keeping first: " + name);
                 return null;
             }
-            p = new PackageSetting(name, codePath, resourcePath, pkgFlags);
+            p = new PackageSetting(name, codePath, resourcePath, vc, pkgFlags);
             p.userId = uid;
             if (addUserIdLP(uid, p, name)) {
                 mPackages.put(name, p);
@@ -5499,7 +5600,7 @@
 
         private PackageSetting getPackageLP(String name,
                 SharedUserSetting sharedUser, File codePath, File resourcePath,
-                int pkgFlags, boolean create, boolean add) {
+                int vc, int pkgFlags, boolean create, boolean add) {
             PackageSetting p = mPackages.get(name);
             if (p != null) {
                 if (!p.codePath.equals(codePath)) {
@@ -5541,7 +5642,7 @@
                 if (!create) {
                     return null;
                 }
-                p = new PackageSetting(name, codePath, resourcePath, pkgFlags);
+                p = new PackageSetting(name, codePath, resourcePath, vc, pkgFlags);
                 p.setTimeStamp(codePath.lastModified());
                 if (sharedUser != null) {
                     p.userId = sharedUser.userId;
@@ -5808,6 +5909,7 @@
             serializer.attribute(null, "name", pkg.name);
             serializer.attribute(null, "codePath", pkg.codePathString);
             serializer.attribute(null, "ts", pkg.getTimeStampStr());
+            serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
             if (!pkg.resourcePathString.equals(pkg.codePathString)) {
                 serializer.attribute(null, "resourcePath", pkg.resourcePathString);
             }
@@ -5851,6 +5953,7 @@
                     (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
                     ? "true" : "false");
             serializer.attribute(null, "ts", pkg.getTimeStampStr());
+            serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
             if (pkg.sharedUser == null) {
                 serializer.attribute(null, "userId",
                         Integer.toString(pkg.userId));
@@ -5866,6 +5969,9 @@
             if(pkg.installStatus == PKG_INSTALL_INCOMPLETE) {
                 serializer.attribute(null, "installStatus", "false");
             }
+            if (pkg.installerPackageName != null) {
+                serializer.attribute(null, "installer", pkg.installerPackageName);
+            }
             pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
             if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
                 serializer.startTag(null, "perms");
@@ -5900,6 +6006,7 @@
                 }
                 serializer.endTag(null, "enabled-components");
             }
+            
             serializer.endTag(null, "package");
         }
         
@@ -6042,7 +6149,7 @@
                 if (idObj != null && idObj instanceof SharedUserSetting) {
                     PackageSetting p = getPackageLP(pp.name,
                             (SharedUserSetting)idObj, pp.codePath, pp.resourcePath,
-                            pp.pkgFlags, true, true);
+                            pp.versionCode, pp.pkgFlags, true, true);
                     if (p == null) {
                         Log.w(TAG, "Unable to create application package for "
                                 + pp.name);
@@ -6161,12 +6268,20 @@
             if(resourcePathStr == null) {
                 resourcePathStr = codePathStr;
             }
+            String version = parser.getAttributeValue(null, "version");
+            int versionCode = 0;
+            if (version != null) {
+                try {
+                    versionCode = Integer.parseInt(version);
+                } catch (NumberFormatException e) {
+                }
+            }
             
             int pkgFlags = 0;
             pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
             PackageSetting ps = new PackageSetting(name, 
                     new File(codePathStr), 
-                    new File(resourcePathStr), pkgFlags);
+                    new File(resourcePathStr), versionCode, pkgFlags);
             String timeStampStr = parser.getAttributeValue(null, "ts");
             if (timeStampStr != null) {
                 try {
@@ -6213,17 +6328,28 @@
             String codePathStr = null;
             String resourcePathStr = null;
             String systemStr = null;
+            String installerPackageName = null;
             int pkgFlags = 0;
             String timeStampStr;
             long timeStamp = 0;
             PackageSettingBase packageSetting = null;
+            String version = null;
+            int versionCode = 0;
             try {
                 name = parser.getAttributeValue(null, "name");
                 idStr = parser.getAttributeValue(null, "userId");
                 sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
                 codePathStr = parser.getAttributeValue(null, "codePath");
                 resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+                version = parser.getAttributeValue(null, "version");
+                if (version != null) {
+                    try {
+                        versionCode = Integer.parseInt(version);
+                    } catch (NumberFormatException e) {
+                    }
+                }
                 systemStr = parser.getAttributeValue(null, "system");
+                installerPackageName = parser.getAttributeValue(null, "installer");
                 if (systemStr != null) {
                     if ("true".equals(systemStr)) {
                         pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
@@ -6256,7 +6382,7 @@
                             + parser.getPositionDescription());
                 } else if (userId > 0) {
                     packageSetting = addPackageLP(name.intern(), new File(codePathStr), 
-                            new File(resourcePathStr), userId, pkgFlags);
+                            new File(resourcePathStr), userId, versionCode, pkgFlags);
                     if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
                             + ": userId=" + userId + " pkg=" + packageSetting);
                     if (packageSetting == null) {
@@ -6272,7 +6398,7 @@
                             ? Integer.parseInt(sharedIdStr) : 0;
                     if (userId > 0) {
                         packageSetting = new PendingPackage(name.intern(), new File(codePathStr),
-                                new File(resourcePathStr), userId, pkgFlags);
+                                new File(resourcePathStr), userId, versionCode, pkgFlags);
                         packageSetting.setTimeStamp(timeStamp, timeStampStr);
                         mPendingPackages.add((PendingPackage) packageSetting);
                         if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
@@ -6297,6 +6423,7 @@
                         + parser.getPositionDescription());
             }
             if (packageSetting != null) {
+                packageSetting.installerPackageName = installerPackageName;
                 final String enabledStr = parser.getAttributeValue(null, "enabled");
                 if (enabledStr != null) {
                     if (enabledStr.equalsIgnoreCase("true")) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 9c6e9dc..c5ea5fa9 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -496,8 +496,10 @@
     }
 
     public void acquireWakeLock(int flags, IBinder lock, String tag) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
         int uid = Binder.getCallingUid();
+        if (uid != Process.myUid()) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+        }
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mLocks) {
@@ -554,14 +556,14 @@
             // by the current state so we never turn it more on than
             // it already is.
             if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
-                reactivateWakeLocksLocked();
+                int oldWakeLockState = mWakeLockState;
+                mWakeLockState = mLocks.reactivateScreenLocksLocked();
                 if (mSpew) {
                     Log.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
-                            + " mLocks.gatherState()=0x"
-                            + Integer.toHexString(mLocks.gatherState())
-                            + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+                            + " mWakeLockState=0x"
+                            + Integer.toHexString(mWakeLockState)
+                            + " previous wakeLockState=0x" + Integer.toHexString(oldWakeLockState));
                 }
-                mWakeLockState = mLocks.gatherState();
             } else {
                 if (mSpew) {
                     Log.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState)
@@ -598,7 +600,10 @@
     }
 
     public void releaseWakeLock(IBinder lock) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+        int uid = Binder.getCallingUid();
+        if (uid != Process.myUid()) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+        }
 
         synchronized (mLocks) {
             releaseWakeLockLocked(lock, false);
@@ -653,17 +658,6 @@
         }
     }
 
-    private void reactivateWakeLocksLocked()
-    {
-        int N = mLocks.size();
-        for (int i=0; i<N; i++) {
-            WakeLock wl = mLocks.get(i);
-            if (isScreenLock(wl.flags)) {
-                mLocks.get(i).activated = true;
-            }
-        }
-    }
-
     private class PokeLock implements IBinder.DeathRecipient
     {
         PokeLock(int p, IBinder b, String t) {
@@ -1752,8 +1746,7 @@
                         Binder.restoreCallingIdentity(ident);
                     }
                     
-                    reactivateWakeLocksLocked();
-                    mWakeLockState = mLocks.gatherState();
+                    mWakeLockState = mLocks.reactivateScreenLocksLocked();
                     setPowerState(mUserState | mWakeLockState, noChangeLights, true);
                     setTimeoutLocked(time, SCREEN_BRIGHT);
                 }
@@ -1944,6 +1937,20 @@
             }
             return result;
         }
+        
+        int reactivateScreenLocksLocked()
+        {
+            int result = 0;
+            int N = this.size();
+            for (int i=0; i<N; i++) {
+                WakeLock wl = this.get(i);
+                if (isScreenLock(wl.flags)) {
+                    wl.activated = true;
+                    result |= wl.minState;
+                }
+            }
+            return result;
+        }
     }
 
     void setPolicy(WindowManagerPolicy p) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1f508a6..8b7260b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -306,6 +306,13 @@
             }
 
             try {
+                Log.i(TAG, "Starting Backup Service");
+                ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
+            } catch (Throwable e) {
+                Log.e(TAG, "Failure starting Backup Service", e);
+            }
+
+            try {
                 Log.i(TAG, "Starting AppWidget Service");
                 appWidget = new AppWidgetService(context);
                 ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 54e77f0..348f0a1 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -96,6 +96,11 @@
     private int mScanLocksAcquired;
     private int mScanLocksReleased;
 
+    private final List<WifiMulticaster> mMulticasters =
+            new ArrayList<WifiMulticaster>();
+    private int mMulticastEnabled;
+    private int mMulticastDisabled;
+
     private final IBatteryStats mBatteryStats;
     
     /**
@@ -165,7 +170,6 @@
      * Character buffer used to parse scan results (optimization)
      */
     private static final int SCAN_RESULT_BUFFER_SIZE = 512;
-    private char[] mScanResultBuffer;
     private boolean mNeedReconfig;
 
     /*
@@ -204,8 +208,6 @@
                 }
             };
 
-        mScanResultBuffer = new char [SCAN_RESULT_BUFFER_SIZE];
-
         HandlerThread wifiThread = new HandlerThread("WifiService");
         wifiThread.start();
         mWifiHandler = new WifiHandler(wifiThread.getLooper());
@@ -1217,61 +1219,13 @@
                     lineBeg = lineEnd + 1;
                     continue;
                 }
-                int lineLen = lineEnd - lineBeg;
-                if (0 < lineLen && lineLen <= SCAN_RESULT_BUFFER_SIZE) {
-                    int scanResultLevel = 0;
-                    /*
-                     * At most one thread should have access to the buffer at a time!
-                     */
-                    synchronized(mScanResultBuffer) {
-                        boolean parsingScanResultLevel = false;
-                        for (int i = lineBeg; i < lineEnd; ++i) {
-                            char ch = reply.charAt(i);
-                            /*
-                             * Assume that the signal level starts with a '-'
-                             */
-                            if (ch == '-') {
-                                /*
-                                 * Skip whatever instances of '-' we may have
-                                 * after we parse the signal level
-                                 */
-                                parsingScanResultLevel = (scanResultLevel == 0);
-                            } else if (parsingScanResultLevel) {
-                                int digit = Character.digit(ch, 10);
-                                if (0 <= digit) {
-                                    scanResultLevel =
-                                        10 * scanResultLevel + digit;
-                                    /*
-                                     * Replace the signal level number in
-                                     * the string with 0's for caching
-                                     */
-                                    ch = '0';
-                                } else {
-                                    /*
-                                     * Reset the flag if we meet a non-digit
-                                     * character
-                                     */
-                                    parsingScanResultLevel = false;
-                                }
-                            }
-                            mScanResultBuffer[i - lineBeg] = ch;
-                        }
-                        if (scanResultLevel != 0) {
-                            ScanResult scanResult = parseScanResult(
-                                new String(mScanResultBuffer, 0, lineLen));
-                            if (scanResult != null) {
-                              scanResult.level = -scanResultLevel;
-                              scanList.add(scanResult);
-                            }
-                        } else if (DBG) {
-                            Log.w(TAG,
-                                  "ScanResult.level=0: misformatted scan result?");
-                        }
-                    }
-                } else if (0 < lineLen) {
-                    if (DBG) {
-                        Log.w(TAG, "Scan result line is too long: " +
-                              (lineEnd - lineBeg) + ", skipping the line!");
+                if (lineEnd > lineBeg) {
+                    String line = reply.substring(lineBeg, lineEnd);
+                    ScanResult scanResult = parseScanResult(line);
+                    if (scanResult != null) {
+                        scanList.add(scanResult);
+                    } else if (DBG) {
+                        Log.w(TAG, "misformatted scan result for: " + line);
                     }
                 }
                 lineBeg = lineEnd + 1;
@@ -1294,21 +1248,29 @@
              * must synchronized here!
              */
             synchronized (mScanResultCache) {
-                scanResult = mScanResultCache.get(line);
-                if (scanResult == null) {
-                    String[] result = scanResultPattern.split(line);
-                    if (3 <= result.length && result.length <= 5) {
-                        // bssid | frequency | level | flags | ssid
-                        int frequency;
-                        int level;
-                        try {
-                            frequency = Integer.parseInt(result[1]);
-                            level = Integer.parseInt(result[2]);
-                        } catch (NumberFormatException e) {
-                            frequency = 0;
-                            level = 0;
-                        }
+                String[] result = scanResultPattern.split(line);
+                if (3 <= result.length && result.length <= 5) {
+                    String bssid = result[0];
+                    // bssid | frequency | level | flags | ssid
+                    int frequency;
+                    int level;
+                    try {
+                        frequency = Integer.parseInt(result[1]);
+                        level = Integer.parseInt(result[2]);
+                        /* some implementations avoid negative values by adding 256
+                         * so we need to adjust for that here.
+                         */
+                        if (level > 0) level -= 256;
+                    } catch (NumberFormatException e) {
+                        frequency = 0;
+                        level = 0;
+                    }
 
+                    // bssid is the hash key
+                    scanResult = mScanResultCache.get(bssid);
+                    if (scanResult != null) {
+                        scanResult.level = level;
+                    } else {
                         /*
                          * The formatting of the results returned by
                          * wpa_supplicant is intended to make the fields
@@ -1341,13 +1303,13 @@
                         if (0 < ssid.trim().length()) {
                             scanResult =
                                 new ScanResult(
-                                    ssid, result[0], flags, level, frequency);
-                            mScanResultCache.put(line, scanResult);
+                                    ssid, bssid, flags, level, frequency);
+                            mScanResultCache.put(bssid, scanResult);
                         }
-                    } else {
-                        Log.w(TAG, "Misformatted scan result text with " +
-                              result.length + " fields: " + line);
                     }
+                } else {
+                    Log.w(TAG, "Misformatted scan result text with " +
+                          result.length + " fields: " + line);
                 }
             }
         }
@@ -1770,21 +1732,9 @@
         }
     }
 
-    private class WifiLock implements IBinder.DeathRecipient {
-        String mTag;
-        int mLockMode;
-        IBinder mBinder;
-
+    private class WifiLock extends WifiDeathRecipient {
         WifiLock(int lockMode, String tag, IBinder binder) {
-            super();
-            mTag = tag;
-            mLockMode = lockMode;
-            mBinder = binder;
-            try {
-                mBinder.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                binderDied();
-            }
+            super(lockMode, tag, binder);
         }
 
         public void binderDied() {
@@ -1794,7 +1744,7 @@
         }
 
         public String toString() {
-            return "WifiLock{" + mTag + " type=" + mLockMode + " binder=" + mBinder + "}";
+            return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
         }
     }
 
@@ -1814,7 +1764,7 @@
                 return WifiManager.WIFI_MODE_FULL;
             }
             for (WifiLock l : mList) {
-                if (l.mLockMode == WifiManager.WIFI_MODE_FULL) {
+                if (l.mMode == WifiManager.WIFI_MODE_FULL) {
                     return WifiManager.WIFI_MODE_FULL;
                 }
             }
@@ -1869,7 +1819,7 @@
         int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
-            switch(wifiLock.mLockMode) {
+            switch(wifiLock.mMode) {
             case WifiManager.WIFI_MODE_FULL:
                 ++mFullLocksAcquired;
                 mBatteryStats.noteFullWifiLockAcquired(uid);
@@ -1905,7 +1855,7 @@
             int uid = Binder.getCallingUid();
             long ident = Binder.clearCallingIdentity();
             try {
-                switch(wifiLock.mLockMode) {
+                switch(wifiLock.mMode) {
                     case WifiManager.WIFI_MODE_FULL:
                         ++mFullLocksReleased;
                         mBatteryStats.noteFullWifiLockReleased(uid);
@@ -1924,4 +1874,110 @@
         updateWifiState();
         return hadLock;
     }
+
+    private abstract class WifiDeathRecipient
+            implements IBinder.DeathRecipient {
+        String mTag;
+        int mMode;
+        IBinder mBinder;
+
+        WifiDeathRecipient(int mode, String tag, IBinder binder) {
+            super();
+            mTag = tag;
+            mMode = mode;
+            mBinder = binder;
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+    }
+
+    private class WifiMulticaster extends WifiDeathRecipient {
+        WifiMulticaster(String tag, IBinder binder) {
+            super(Binder.getCallingUid(), tag, binder);
+        }
+
+        public void binderDied() {
+            Log.e(TAG, "WifiMulticaster binderDied");
+            synchronized (mMulticasters) {
+                int i = mMulticasters.indexOf(this);
+                if (i != -1) {
+                    removeMulticasterLocked(i, mMode);
+                }
+            }
+        }
+
+        public String toString() {
+            return "WifiMulticaster{" + mTag + " binder=" + mBinder + "}";
+        }
+
+        public int getUid() {
+            return mMode;
+        }
+    }
+
+    public void enableWifiMulticast(IBinder binder, String tag) {
+        enforceChangePermission();
+
+        synchronized (mMulticasters) {
+            mMulticastEnabled++;
+            mMulticasters.add(new WifiMulticaster(tag, binder));
+            // Note that we could call stopPacketFiltering only when
+            // our new size == 1 (first call), but this function won't
+            // be called often and by making the stopPacket call each
+            // time we're less fragile and self-healing.
+            WifiNative.stopPacketFiltering();
+        }
+
+        int uid = Binder.getCallingUid();
+        Long ident = Binder.clearCallingIdentity();
+        try {
+            mBatteryStats.noteWifiMulticastEnabled(uid);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void disableWifiMulticast() {
+        enforceChangePermission();
+
+        int uid = Binder.getCallingUid();
+        synchronized (mMulticasters) {
+            mMulticastDisabled++;
+            int size = mMulticasters.size();
+            for (int i = size - 1; i >= 0; i--) {
+                WifiMulticaster m = mMulticasters.get(i);
+                if ((m != null) && (m.getUid() == uid)) {
+                    removeMulticasterLocked(i, uid);
+                }
+            }
+        }
+    }
+
+    private void removeMulticasterLocked(int i, int uid)
+    {
+        mMulticasters.remove(i);
+        if (mMulticasters.size() == 0) {
+            WifiNative.startPacketFiltering();
+        }
+
+        Long ident = Binder.clearCallingIdentity();
+        try {
+            mBatteryStats.noteWifiMulticastDisabled(uid);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public boolean isWifiMulticastEnabled() {
+        enforceAccessPermission();
+
+        synchronized (mMulticasters) {
+            return (mMulticasters.size() > 0);
+        }
+    }
 }
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index fe97b93..9443a95 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -75,14 +75,6 @@
     private static final boolean V = false || Config.LOGV;
     private static final boolean D = true || Config.LOGD;
     
-    /*
-     * When this was "net.dns1", sometimes the mobile data's DNS was seen
-     * instead due to a race condition. All we really care about is the
-     * DHCP-replied DNS server anyway.
-     */
-    /** The system property whose value provides the current DNS address. */
-    private static final String SYSTEMPROPERTY_KEY_DNS = "dhcp.tiwlan0.dns1";
-
     private Context mContext;
     private ContentResolver mContentResolver;
     private WifiStateTracker mWifiStateTracker;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0b1ddc8..3fa5baf 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -77,7 +77,6 @@
 import android.os.SystemProperties;
 import android.os.TokenWatcher;
 import android.provider.Settings;
-import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -137,7 +136,7 @@
     
     static final boolean PROFILE_ORIENTATION = false;
     static final boolean BLUR = true;
-    static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    static final boolean localLOGV = DEBUG;
     
     static final int LOG_WM_NO_SURFACE_MEMORY = 31000;
     
@@ -180,6 +179,25 @@
     static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
     static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
     
+    /** The minimum time between dispatching touch events. */
+    int mMinWaitTimeBetweenTouchEvents = 1000 / 35;
+
+    // Last touch event time
+    long mLastTouchEventTime = 0;
+    
+    // Last touch event type
+    int mLastTouchEventType = OTHER_EVENT;
+    
+    // Time to wait before calling useractivity again. This saves CPU usage
+    // when we get a flood of touch events.
+    static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000;
+
+    // Last time we call user activity
+    long mLastUserActivityCallTime = 0;
+
+    // Last time we updated battery stats 
+    long mLastBatteryStatsCallTime = 0;
+    
     private static final String SYSTEM_SECURE = "ro.secure";
 
     /**
@@ -2023,7 +2041,7 @@
             wtoken.appFullscreen = fullscreen;
             wtoken.requestedOrientation = requestedOrientation;
             mAppTokens.add(addPos, wtoken);
-            if (Config.LOGV) Log.v(TAG, "Adding new app token: " + wtoken);
+            if (localLOGV) Log.v(TAG, "Adding new app token: " + wtoken);
             mTokenMap.put(token.asBinder(), wtoken);
             mTokenList.add(wtoken);
             
@@ -2079,13 +2097,16 @@
             int pos = mAppTokens.size() - 1;
             int curGroup = 0;
             int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            boolean findingBehind = false;
             boolean haveGroup = false;
             boolean lastFullscreen = false;
             while (pos >= 0) {
                 AppWindowToken wtoken = mAppTokens.get(pos);
                 pos--;
-                // if we're about to tear down this window, don't use it for orientation
-                if (!wtoken.hidden && wtoken.hiddenRequested) {
+                // if we're about to tear down this window and not seek for
+                // the behind activity, don't use it for orientation
+                if (!findingBehind
+                        && (!wtoken.hidden && wtoken.hiddenRequested)) {
                     continue;
                 }
 
@@ -2109,10 +2130,12 @@
                     }
                 }
                 int or = wtoken.requestedOrientation;
-                // If this application is fullscreen, then just take whatever
+                // If this application is fullscreen, and didn't explicitly say
+                // to use the orientation behind it, then just take whatever
                 // orientation it has and ignores whatever is under it.
                 lastFullscreen = wtoken.appFullscreen;
-                if (lastFullscreen) {
+                if (lastFullscreen 
+                        && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
                     return or;
                 }
                 // If this application has requested an explicit orientation,
@@ -2124,6 +2147,7 @@
                         or == ActivityInfo.SCREEN_ORIENTATION_USER) {
                     return or;
                 }
+                findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
             }
             return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
@@ -3689,9 +3713,20 @@
     // -------------------------------------------------------------
 
     private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
-        if (targetWin == null ||
-                targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false, eventType);
+        long curTime = SystemClock.uptimeMillis();
+
+        if (eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
+            if (mLastTouchEventType == eventType &&
+                    (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
+                return;
+            }
+            mLastUserActivityCallTime = curTime;
+            mLastTouchEventType = eventType;
+        }
+
+        if (targetWin == null
+                || targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
+            mPowerManager.userActivity(curTime, false, eventType, false);
         }
     }
 
@@ -3759,7 +3794,7 @@
             // events in such a way, since this means the user is moving the
             // pointer without actually pressing down.  All other cases should
             // be atypical, so let's log them.
-            if (ev.getAction() != MotionEvent.ACTION_MOVE) {
+            if (action != MotionEvent.ACTION_MOVE) {
                 Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());
             }
             if (qev != null) {
@@ -3846,7 +3881,39 @@
                 return false;
             }
         } //end if target
-        
+
+        // TODO remove once we settle on a value or make it app specific
+        if (action == MotionEvent.ACTION_DOWN) {
+            int max_events_per_sec = 35;
+            try {
+                max_events_per_sec = Integer.parseInt(SystemProperties
+                        .get("windowsmgr.max_events_per_sec"));
+                if (max_events_per_sec < 1) {
+                    max_events_per_sec = 35;
+                }
+            } catch (NumberFormatException e) {
+            }
+            mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
+        }
+
+        /*
+         * Throttle events to minimize CPU usage when there's a flood of events
+         * e.g. constant contact with the screen
+         */
+        if (action == MotionEvent.ACTION_MOVE) {
+            long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents;
+            long now = SystemClock.uptimeMillis();
+            if (now < nextEventTime) {
+                try {
+                    Thread.sleep(nextEventTime - now);
+                } catch (InterruptedException e) {
+                }
+                mLastTouchEventTime = nextEventTime;
+            } else {
+                mLastTouchEventTime = now;
+            }
+        }
+
         synchronized(mWindowMap) {
             if (qev != null && action == MotionEvent.ACTION_MOVE) {
                 mKeyWaiter.bindTargetWindowLocked(target,
@@ -4801,14 +4868,11 @@
                     mPaused = true;
                 } else {
                     if (mLastWin == null) {
-                        if (Config.LOGI) Log.i(
-                            TAG, "Key dispatching not paused: no last window.");
+                        Log.i(TAG, "Key dispatching not paused: no last window.");
                     } else if (mFinished) {
-                        if (Config.LOGI) Log.i(
-                            TAG, "Key dispatching not paused: finished last key.");
+                        Log.i(TAG, "Key dispatching not paused: finished last key.");
                     } else {
-                        if (Config.LOGI) Log.i(
-                            TAG, "Key dispatching not paused: window in higher layer.");
+                        Log.i(TAG, "Key dispatching not paused: window in higher layer.");
                     }
                 }
                 */
@@ -4926,7 +4990,7 @@
                     }
                     if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
                         mPowerManager.userActivity(event.when, false,
-                                LocalPowerManager.BUTTON_EVENT);
+                                LocalPowerManager.BUTTON_EVENT, false);
                     }
                     
                     if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
@@ -5086,11 +5150,17 @@
                             eventType = LocalPowerManager.OTHER_EVENT;
                         }
                         try {
-                            mBatteryStats.noteInputEvent();
+                            long now = SystemClock.uptimeMillis();
+
+                            if ((now - mLastBatteryStatsCallTime)
+                                    >= MIN_TIME_BETWEEN_USERACTIVITIES) {
+                                mLastBatteryStatsCallTime = now;
+                                mBatteryStats.noteInputEvent();
+                            }
                         } catch (RemoteException e) {
                             // Ignore
                         }
-                        mPowerManager.userActivity(curTime, false, eventType);
+                        mPowerManager.userActivity(curTime, false, eventType, false);
                         switch (ev.classType) {
                             case RawInputEvent.CLASS_KEYBOARD:
                                 KeyEvent ke = (KeyEvent)ev.event;
@@ -5179,6 +5249,7 @@
         final IInputContext mInputContext;
         final int mUid;
         final int mPid;
+        final String mStringName;
         SurfaceSession mSurfaceSession;
         int mNumWindow = 0;
         boolean mClientDead = false;
@@ -5202,6 +5273,14 @@
             mInputContext = inputContext;
             mUid = Binder.getCallingUid();
             mPid = Binder.getCallingPid();
+            StringBuilder sb = new StringBuilder();
+            sb.append("Session{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" uid ");
+            sb.append(mUid);
+            sb.append("}");
+            mStringName = sb.toString();
+            
             synchronized (mWindowMap) {
                 if (mInputMethodManager == null && mHaveInputMethods) {
                     IBinder b = ServiceManager.getService(
@@ -5381,20 +5460,24 @@
         }
         
         void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + this);
-            pw.println(prefix + "mNumWindow=" + mNumWindow
-                    + " mClientDead=" + mClientDead
-                    + " mSurfaceSession=" + mSurfaceSession);
-            pw.println(prefix + "mPendingPointerWindow=" + mPendingPointerWindow
-                    + " mPendingPointerMove=" + mPendingPointerMove);
-            pw.println(prefix + "mPendingTrackballWindow=" + mPendingTrackballWindow
-                    + " mPendingTrackballMove=" + mPendingTrackballMove);
+            pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
+                    pw.print(" mClientDead="); pw.print(mClientDead);
+                    pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
+            if (mPendingPointerWindow != null || mPendingPointerMove != null) {
+                pw.print(prefix);
+                        pw.print("mPendingPointerWindow="); pw.print(mPendingPointerWindow);
+                        pw.print(" mPendingPointerMove="); pw.println(mPendingPointerMove);
+            }
+            if (mPendingTrackballWindow != null || mPendingTrackballMove != null) {
+                pw.print(prefix);
+                        pw.print("mPendingTrackballWindow="); pw.print(mPendingTrackballWindow);
+                        pw.print(" mPendingTrackballMove="); pw.println(mPendingTrackballMove);
+            }
         }
 
         @Override
         public String toString() {
-            return "Session{"
-                + Integer.toHexString(System.identityHashCode(this)) + "}";
+            return mStringName;
         }
     }
 
@@ -6462,67 +6545,114 @@
         }
 
         void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + this);
-            pw.println(prefix + "mSession=" + mSession
-                  + " mClient=" + mClient.asBinder());
-            pw.println(prefix + "mAttrs=" + mAttrs);
-            pw.println(prefix + "mAttachedWindow=" + mAttachedWindow
-                    + " mLayoutAttached=" + mLayoutAttached
-                    + " mIsImWindow=" + mIsImWindow);
-            pw.println(prefix + "mBaseLayer=" + mBaseLayer
-                  + " mSubLayer=" + mSubLayer
-                  + " mAnimLayer=" + mLayer + "+"
-                  + (mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
-                          : (mAppToken != null ? mAppToken.animLayerAdjustment : 0))
-                  + "=" + mAnimLayer
-                  + " mLastLayer=" + mLastLayer);
-            pw.println(prefix + "mSurface=" + mSurface);
-            pw.println(prefix + "mToken=" + mToken);
-            pw.println(prefix + "mRootToken=" + mRootToken);
-            pw.println(prefix + "mAppToken=" + mAppToken);
-            pw.println(prefix + "mTargetAppToken=" + mTargetAppToken);
-            pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
-                  + " mPolicyVisibility=" + mPolicyVisibility
-                  + " (after=" + mPolicyVisibilityAfterAnim
-                  + ") mAttachedHidden=" + mAttachedHidden
-                  + " mLastHidden=" + mLastHidden
-                  + " mHaveFrame=" + mHaveFrame);
-            pw.println(prefix + "Requested w=" + mRequestedWidth + " h=" + mRequestedHeight
-                  + " x=" + mReqXPos + " y=" + mReqYPos);
-            pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString()
-                    + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString()
-                    + " mTouchableInsets=" + mTouchableInsets
-                    + " pending=" + mGivenInsetsPending);
-            pw.println(prefix + "mShownFrame=" + mShownFrame.toShortString()
-                  + " last=" + mLastShownFrame.toShortString());
-            pw.println(prefix + "mFrame=" + mFrame.toShortString()
-                    + " last=" + mLastFrame.toShortString());
-            pw.println(prefix + "mContainingFrame=" + mContainingFrame.toShortString()
-                    + " mDisplayFrame=" + mDisplayFrame.toShortString());
-            pw.println(prefix + "mContentFrame=" + mContentFrame.toShortString()
-                    + " mVisibleFrame=" + mVisibleFrame.toShortString());
-            pw.println(prefix + "mContentInsets=" + mContentInsets.toShortString()
-                    + " last=" + mLastContentInsets.toShortString()
-                    + " mVisibleInsets=" + mVisibleInsets.toShortString()
-                    + " last=" + mLastVisibleInsets.toShortString());
-            pw.println(prefix + "mShownAlpha=" + mShownAlpha
-                  + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha);
-            pw.println(prefix + "mAnimating=" + mAnimating
-                    + " mLocalAnimating=" + mLocalAnimating
-                    + " mAnimationIsEntrance=" + mAnimationIsEntrance
-                    + " mAnimation=" + mAnimation);
-            pw.println(prefix + "XForm: has=" + mHasTransformation
-                    + " " + mTransformation.toShortString());
-            pw.println(prefix + "mDrawPending=" + mDrawPending
-                  + " mCommitDrawPending=" + mCommitDrawPending
-                  + " mReadyToShow=" + mReadyToShow
-                  + " mHasDrawn=" + mHasDrawn);
-            pw.println(prefix + "mExiting=" + mExiting
-                    + " mRemoveOnExit=" + mRemoveOnExit
-                    + " mDestroying=" + mDestroying
-                    + " mRemoved=" + mRemoved);
-            pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
-                    + " mAppFreezing=" + mAppFreezing);
+            StringBuilder sb = new StringBuilder(64);
+            
+            pw.print(prefix); pw.print("mSession="); pw.print(mSession);
+                    pw.print(" mClient="); pw.println(mClient.asBinder());
+            pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs);
+            if (mAttachedWindow != null || mLayoutAttached) {
+                pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow);
+                        pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
+            }
+            if (mIsImWindow) {
+                pw.print(prefix); pw.print("mIsImWindow="); pw.println(mIsImWindow);
+            }
+            pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
+                    pw.print(" mSubLayer="); pw.print(mSubLayer);
+                    pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
+                    pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
+                          : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
+                    pw.print("="); pw.print(mAnimLayer);
+                    pw.print(" mLastLayer="); pw.println(mLastLayer);
+            if (mSurface != null) {
+                pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
+            }
+            pw.print(prefix); pw.print("mToken="); pw.println(mToken);
+            pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
+            if (mAppToken != null) {
+                pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
+            }
+            if (mTargetAppToken != null) {
+                pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken);
+            }
+            pw.print(prefix); pw.print("mViewVisibility=0x");
+                    pw.print(Integer.toHexString(mViewVisibility));
+                    pw.print(" mLastHidden="); pw.print(mLastHidden);
+                    pw.print(" mHaveFrame="); pw.println(mHaveFrame);
+            if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) {
+                pw.print(prefix); pw.print("mPolicyVisibility=");
+                        pw.print(mPolicyVisibility);
+                        pw.print(" mPolicyVisibilityAfterAnim=");
+                        pw.print(mPolicyVisibilityAfterAnim);
+                        pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
+            }
+            pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
+                    pw.print(" h="); pw.print(mRequestedHeight);
+                    pw.print(" x="); pw.print(mReqXPos);
+                    pw.print(" y="); pw.println(mReqYPos);
+            pw.print(prefix); pw.print("mGivenContentInsets=");
+                    mGivenContentInsets.printShortString(pw);
+                    pw.print(" mGivenVisibleInsets=");
+                    mGivenVisibleInsets.printShortString(pw);
+                    pw.println();
+            if (mTouchableInsets != 0 || mGivenInsetsPending) {
+                pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets);
+                        pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending);
+            }
+            pw.print(prefix); pw.print("mShownFrame=");
+                    mShownFrame.printShortString(pw);
+                    pw.print(" last="); mLastShownFrame.printShortString(pw);
+                    pw.println();
+            pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
+                    pw.print(" last="); mLastFrame.printShortString(pw);
+                    pw.println();
+            pw.print(prefix); pw.print("mContainingFrame=");
+                    mContainingFrame.printShortString(pw);
+                    pw.print(" mDisplayFrame=");
+                    mDisplayFrame.printShortString(pw);
+                    pw.println();
+            pw.print(prefix); pw.print("mContentFrame="); mContentFrame.printShortString(pw);
+                    pw.print(" mVisibleFrame="); mVisibleFrame.printShortString(pw);
+                    pw.println();
+            pw.print(prefix); pw.print("mContentInsets="); mContentInsets.printShortString(pw);
+                    pw.print(" last="); mLastContentInsets.printShortString(pw);
+                    pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw);
+                    pw.print(" last="); mLastVisibleInsets.printShortString(pw);
+                    pw.println();
+            if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
+                pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
+                        pw.print(" mAlpha="); pw.print(mAlpha);
+                        pw.print(" mLastAlpha="); pw.println(mLastAlpha);
+            }
+            if (mAnimating || mLocalAnimating || mAnimationIsEntrance
+                    || mAnimation != null) {
+                pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
+                        pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
+                        pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
+                        pw.print(" mAnimation="); pw.println(mAnimation);
+            }
+            if (mHasTransformation || mHasLocalTransformation) {
+                pw.print(prefix); pw.print("XForm: has=");
+                        pw.print(mHasTransformation);
+                        pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
+                        pw.print(" "); mTransformation.printShortString(pw);
+                        pw.println();
+            }
+            pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending);
+                    pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending);
+                    pw.print(" mReadyToShow="); pw.print(mReadyToShow);
+                    pw.print(" mHasDrawn="); pw.println(mHasDrawn);
+            if (mExiting || mRemoveOnExit || mDestroying || mRemoved) {
+                pw.print(prefix); pw.print("mExiting="); pw.print(mExiting);
+                        pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit);
+                        pw.print(" mDestroying="); pw.print(mDestroying);
+                        pw.print(" mRemoved="); pw.println(mRemoved);
+            }
+            if (mOrientationChanging || mAppFreezing) {
+                pw.print(prefix); pw.print("mOrientationChanging=");
+                        pw.print(mOrientationChanging);
+                        pw.print(" mAppFreezing="); pw.println(mAppFreezing);
+            }
         }
 
         @Override
@@ -6548,6 +6678,9 @@
         // not be removed when all windows are removed.
         final boolean explicit;
         
+        // For printing.
+        String stringName;
+        
         // If this is an AppWindowToken, this is non-null.
         AppWindowToken appWindowToken;
         
@@ -6570,18 +6703,23 @@
         }
 
         void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + this);
-            pw.println(prefix + "token=" + token);
-            pw.println(prefix + "windows=" + windows);
-            pw.println(prefix + "windowType=" + windowType + " hidden=" + hidden
-                    + " hasVisible=" + hasVisible);
+            pw.print(prefix); pw.print("token="); pw.println(token);
+            pw.print(prefix); pw.print("windows="); pw.println(windows);
+            pw.print(prefix); pw.print("windowType="); pw.print(windowType);
+                    pw.print(" hidden="); pw.print(hidden);
+                    pw.print(" hasVisible="); pw.println(hasVisible);
         }
 
         @Override
         public String toString() {
-            return "WindowToken{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " token=" + token + "}";
+            if (stringName == null) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("WindowToken{");
+                sb.append(Integer.toHexString(System.identityHashCode(this)));
+                sb.append(" token="); sb.append(token); sb.append('}');
+                stringName = sb.toString();
+            }
+            return stringName;
         }
     };
 
@@ -6869,38 +7007,66 @@
         
         void dump(PrintWriter pw, String prefix) {
             super.dump(pw, prefix);
-            pw.println(prefix + "app=" + (appToken != null));
-            pw.println(prefix + "allAppWindows=" + allAppWindows);
-            pw.println(prefix + "groupId=" + groupId
-                    + " requestedOrientation=" + requestedOrientation);
-            pw.println(prefix + "hiddenRequested=" + hiddenRequested
-                    + " clientHidden=" + clientHidden
-                    + " willBeHidden=" + willBeHidden
-                    + " reportedVisible=" + reportedVisible);
-            pw.println(prefix + "paused=" + paused
-                    + " freezingScreen=" + freezingScreen);
-            pw.println(prefix + "numInterestingWindows=" + numInterestingWindows
-                    + " numDrawnWindows=" + numDrawnWindows
-                    + " inPendingTransaction=" + inPendingTransaction
-                    + " allDrawn=" + allDrawn);
-            pw.println(prefix + "animating=" + animating
-                    + " animation=" + animation);
-            pw.println(prefix + "animLayerAdjustment=" + animLayerAdjustment
-                    + " transformation=" + transformation.toShortString());
-            pw.println(prefix + "startingData=" + startingData
-                    + " removed=" + removed
-                    + " firstWindowDrawn=" + firstWindowDrawn);
-            pw.println(prefix + "startingWindow=" + startingWindow
-                    + " startingView=" + startingView
-                    + " startingDisplayed=" + startingDisplayed
-                    + " startingMoved" + startingMoved);
+            if (appToken != null) {
+                pw.print(prefix); pw.println("app=true");
+            }
+            if (allAppWindows.size() > 0) {
+                pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
+            }
+            pw.print(prefix); pw.print("groupId="); pw.print(groupId);
+                    pw.print(" requestedOrientation="); pw.println(requestedOrientation);
+            pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
+                    pw.print(" clientHidden="); pw.print(clientHidden);
+                    pw.print(" willBeHidden="); pw.print(willBeHidden);
+                    pw.print(" reportedVisible="); pw.println(reportedVisible);
+            if (paused || freezingScreen) {
+                pw.print(prefix); pw.print("paused="); pw.print(paused);
+                        pw.print(" freezingScreen="); pw.println(freezingScreen);
+            }
+            if (numInterestingWindows != 0 || numDrawnWindows != 0
+                    || inPendingTransaction || allDrawn) {
+                pw.print(prefix); pw.print("numInterestingWindows=");
+                        pw.print(numInterestingWindows);
+                        pw.print(" numDrawnWindows="); pw.print(numDrawnWindows);
+                        pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
+                        pw.print(" allDrawn="); pw.println(allDrawn);
+            }
+            if (animating || animation != null) {
+                pw.print(prefix); pw.print("animating="); pw.print(animating);
+                        pw.print(" animation="); pw.println(animation);
+            }
+            if (animLayerAdjustment != 0) {
+                pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
+            }
+            if (hasTransformation) {
+                pw.print(prefix); pw.print("hasTransformation="); pw.print(hasTransformation);
+                        pw.print(" transformation="); transformation.printShortString(pw);
+                        pw.println();
+            }
+            if (startingData != null || removed || firstWindowDrawn) {
+                pw.print(prefix); pw.print("startingData="); pw.print(startingData);
+                        pw.print(" removed="); pw.print(removed);
+                        pw.print(" firstWindowDrawn="); pw.println(firstWindowDrawn);
+            }
+            if (startingWindow != null || startingView != null
+                    || startingDisplayed || startingMoved) {
+                pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
+                        pw.print(" startingView="); pw.print(startingView);
+                        pw.print(" startingDisplayed="); pw.print(startingDisplayed);
+                        pw.print(" startingMoved"); pw.println(startingMoved);
+            }
         }
 
         @Override
         public String toString() {
-            return "AppWindowToken{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " token=" + token + "}";
+            if (stringName == null) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("AppWindowToken{");
+                sb.append(Integer.toHexString(System.identityHashCode(this)));
+                sb.append(" token="); sb.append(token); sb.append('}');
+                stringName = sb.toString();
+            }
+            return stringName;
         }
     }
     
@@ -7423,7 +7589,7 @@
     private boolean mInLayout = false;
     private final void performLayoutAndPlaceSurfacesLocked() {
         if (mInLayout) {
-            if (Config.DEBUG) {
+            if (DEBUG) {
                 throw new RuntimeException("Recursive call!");
             }
             Log.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout");
@@ -8656,7 +8822,8 @@
             pw.println("Current Window Manager state:");
             for (int i=mWindows.size()-1; i>=0; i--) {
                 WindowState w = (WindowState)mWindows.get(i);
-                pw.println("  Window #" + i + ":");
+                pw.print("  Window #"); pw.print(i); pw.print(' ');
+                        pw.print(w); pw.println(":");
                 w.dump(pw, "    ");
             }
             if (mInputMethodDialogs.size() > 0) {
@@ -8664,7 +8831,7 @@
                 pw.println("  Input method dialogs:");
                 for (int i=mInputMethodDialogs.size()-1; i>=0; i--) {
                     WindowState w = mInputMethodDialogs.get(i);
-                    pw.println("  IM Dialog #" + i + ": " + w);
+                    pw.print("  IM Dialog #"); pw.print(i); pw.print(": "); pw.println(w);
                 }
             }
             if (mPendingRemove.size() > 0) {
@@ -8672,7 +8839,8 @@
                 pw.println("  Remove pending for:");
                 for (int i=mPendingRemove.size()-1; i>=0; i--) {
                     WindowState w = mPendingRemove.get(i);
-                    pw.println("  Remove #" + i + ":");
+                    pw.print("  Remove #"); pw.print(i); pw.print(' ');
+                            pw.print(w); pw.println(":");
                     w.dump(pw, "    ");
                 }
             }
@@ -8681,7 +8849,8 @@
                 pw.println("  Windows force removing:");
                 for (int i=mForceRemoves.size()-1; i>=0; i--) {
                     WindowState w = mForceRemoves.get(i);
-                    pw.println("  Removing #" + i + ":");
+                    pw.print("  Removing #"); pw.print(i); pw.print(' ');
+                            pw.print(w); pw.println(":");
                     w.dump(pw, "    ");
                 }
             }
@@ -8690,7 +8859,8 @@
                 pw.println("  Windows waiting to destroy their surface:");
                 for (int i=mDestroySurface.size()-1; i>=0; i--) {
                     WindowState w = mDestroySurface.get(i);
-                    pw.println("  Destroy #" + i + ":");
+                    pw.print("  Destroy #"); pw.print(i); pw.print(' ');
+                            pw.print(w); pw.println(":");
                     w.dump(pw, "    ");
                 }
             }
@@ -8699,7 +8869,8 @@
                 pw.println("  Windows losing focus:");
                 for (int i=mLosingFocus.size()-1; i>=0; i--) {
                     WindowState w = mLosingFocus.get(i);
-                    pw.println("  Losing #" + i + ":");
+                    pw.print("  Losing #"); pw.print(i); pw.print(' ');
+                            pw.print(w); pw.println(":");
                     w.dump(pw, "    ");
                 }
             }
@@ -8709,7 +8880,7 @@
                 Iterator<Session> it = mSessions.iterator();
                 while (it.hasNext()) {
                     Session s = it.next();
-                    pw.println("  Session " + s);
+                    pw.print("  Session "); pw.print(s); pw.println(':');
                     s.dump(pw, "    ");
                 }
             }
@@ -8719,7 +8890,7 @@
                 Iterator<WindowToken> it = mTokenMap.values().iterator();
                 while (it.hasNext()) {
                     WindowToken token = it.next();
-                    pw.println("  Token " + token.token);
+                    pw.print("  Token "); pw.print(token.token); pw.println(':');
                     token.dump(pw, "    ");
                 }
             }
@@ -8727,14 +8898,16 @@
                 pw.println(" ");
                 pw.println("  Window token list:");
                 for (int i=0; i<mTokenList.size(); i++) {
-                    pw.println("  WindowToken #" + i + ": " + mTokenList.get(i));
+                    pw.print("  #"); pw.print(i); pw.print(": ");
+                            pw.println(mTokenList.get(i));
                 }
             }
             if (mAppTokens.size() > 0) {
                 pw.println(" ");
                 pw.println("  Application tokens in Z order:");
                 for (int i=mAppTokens.size()-1; i>=0; i--) {
-                    pw.println("  AppWindowToken #" + i + ": " + mAppTokens.get(i));
+                    pw.print("  App #"); pw.print(i); pw.print(": ");
+                            pw.println(mAppTokens.get(i));
                 }
             }
             if (mFinishedStarting.size() > 0) {
@@ -8742,7 +8915,8 @@
                 pw.println("  Finishing start of application tokens:");
                 for (int i=mFinishedStarting.size()-1; i>=0; i--) {
                     WindowToken token = mFinishedStarting.get(i);
-                    pw.println("  Finish Starting App Token #" + i + ":");
+                    pw.print("  Finished Starting #"); pw.print(i);
+                            pw.print(' '); pw.print(token); pw.println(':');
                     token.dump(pw, "    ");
                 }
             }
@@ -8751,7 +8925,8 @@
                 pw.println("  Exiting tokens:");
                 for (int i=mExitingTokens.size()-1; i>=0; i--) {
                     WindowToken token = mExitingTokens.get(i);
-                    pw.println("  Exiting Token #" + i + ":");
+                    pw.print("  Exiting #"); pw.print(i);
+                            pw.print(' '); pw.print(token); pw.println(':');
                     token.dump(pw, "    ");
                 }
             }
@@ -8760,54 +8935,59 @@
                 pw.println("  Exiting application tokens:");
                 for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
                     WindowToken token = mExitingAppTokens.get(i);
-                    pw.println("  Exiting App Token #" + i + ":");
+                    pw.print("  Exiting App #"); pw.print(i);
+                            pw.print(' '); pw.print(token); pw.println(':');
                     token.dump(pw, "    ");
                 }
             }
             pw.println(" ");
-            pw.println("  mCurrentFocus=" + mCurrentFocus);
-            pw.println("  mLastFocus=" + mLastFocus);
-            pw.println("  mFocusedApp=" + mFocusedApp);
-            pw.println("  mInputMethodTarget=" + mInputMethodTarget);
-            pw.println("  mInputMethodWindow=" + mInputMethodWindow);
-            pw.println("  mInTouchMode=" + mInTouchMode);
-            pw.println("  mSystemBooted=" + mSystemBooted
-                    + " mDisplayEnabled=" + mDisplayEnabled);
-            pw.println("  mLayoutNeeded=" + mLayoutNeeded
-                    + " 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);
-            pw.println("  mRotation=" + mRotation
-                    + ", mForcedAppOrientation=" + mForcedAppOrientation
-                    + ", mRequestedRotation=" + mRequestedRotation);
-            pw.println("  mAnimationPending=" + mAnimationPending
-                    + " mWindowAnimationScale=" + mWindowAnimationScale
-                    + " mTransitionWindowAnimationScale=" + mTransitionAnimationScale);
-            pw.println("  mNextAppTransition=0x"
-                    + Integer.toHexString(mNextAppTransition)
-                    + ", mAppTransitionReady=" + mAppTransitionReady
-                    + ", mAppTransitionTimeout=" + mAppTransitionTimeout);
-            pw.println("  mStartingIconInTransition=" + mStartingIconInTransition
-                    + ", mSkipAppTransitionAnimation=" + mSkipAppTransitionAnimation);
-            pw.println("  mOpeningApps=" + mOpeningApps);
-                    pw.println("  mClosingApps=" + mClosingApps);
-            pw.println("  DisplayWidth=" + mDisplay.getWidth()
-                    + " DisplayHeight=" + mDisplay.getHeight());
+            pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
+            pw.print("  mLastFocus="); pw.println(mLastFocus);
+            pw.print("  mFocusedApp="); pw.println(mFocusedApp);
+            pw.print("  mInputMethodTarget="); pw.println(mInputMethodTarget);
+            pw.print("  mInputMethodWindow="); pw.println(mInputMethodWindow);
+            pw.print("  mInTouchMode="); pw.println(mInTouchMode);
+            pw.print("  mSystemBooted="); pw.print(mSystemBooted);
+                    pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
+            pw.print("  mLayoutNeeded="); pw.print(mLayoutNeeded);
+                    pw.print(" mBlurShown="); pw.println(mBlurShown);
+            pw.print("  mDimShown="); pw.print(mDimShown);
+                    pw.print(" current="); pw.print(mDimCurrentAlpha);
+                    pw.print(" target="); pw.print(mDimTargetAlpha);
+                    pw.print(" delta="); pw.print(mDimDeltaPerMs);
+                    pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
+            pw.print("  mInputMethodAnimLayerAdjustment=");
+                    pw.println(mInputMethodAnimLayerAdjustment);
+            pw.print("  mDisplayFrozen="); pw.print(mDisplayFrozen);
+                    pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
+                    pw.print(" mAppsFreezingScreen="); pw.println(mAppsFreezingScreen);
+            pw.print("  mRotation="); pw.print(mRotation);
+                    pw.print(", mForcedAppOrientation="); pw.print(mForcedAppOrientation);
+                    pw.print(", mRequestedRotation="); pw.println(mRequestedRotation);
+            pw.print("  mAnimationPending="); pw.print(mAnimationPending);
+                    pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
+                    pw.print(" mTransitionWindowAnimationScale="); pw.println(mTransitionAnimationScale);
+            pw.print("  mNextAppTransition=0x");
+                    pw.print(Integer.toHexString(mNextAppTransition));
+                    pw.print(", mAppTransitionReady="); pw.print(mAppTransitionReady);
+                    pw.print(", mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
+            pw.print("  mStartingIconInTransition="); pw.print(mStartingIconInTransition);
+                    pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
+            if (mOpeningApps.size() > 0) {
+                pw.print("  mOpeningApps="); pw.println(mOpeningApps);
+            }
+            if (mClosingApps.size() > 0) {
+                pw.print("  mClosingApps="); pw.println(mClosingApps);
+            }
+            pw.print("  DisplayWidth="); pw.print(mDisplay.getWidth());
+                    pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
             pw.println("  KeyWaiter state:");
-            pw.println("    mLastWin=" + mKeyWaiter.mLastWin
-                    + " mLastBinder=" + mKeyWaiter.mLastBinder);
-            pw.println("    mFinished=" + mKeyWaiter.mFinished
-                    + " mGotFirstWindow=" + mKeyWaiter.mGotFirstWindow
-                    + " mEventDispatching=" + mKeyWaiter.mEventDispatching
-                    + " mTimeToSwitch=" + mKeyWaiter.mTimeToSwitch);
+            pw.print("    mLastWin="); pw.print(mKeyWaiter.mLastWin);
+                    pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
+            pw.print("    mFinished="); pw.print(mKeyWaiter.mFinished);
+                    pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
+                    pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
+                    pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
         }
     }
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d676c00..9471eff 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -169,6 +169,10 @@
     static final int LOG_BOOT_PROGRESS_AMS_READY = 3040;
     static final int LOG_BOOT_PROGRESS_ENABLE_SCREEN = 3050;
 
+    // The flags that are set for all calls we make to the package manager.
+    static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES
+            | PackageManager.GET_SUPPORTS_DENSITIES;
+    
     private static final String SYSTEM_SECURE = "ro.secure";
 
     // This is the maximum number of application processes we would like
@@ -682,6 +686,11 @@
     HashMap<String, IBinder> mAppBindArgs;
 
     /**
+     * Temporary to avoid allocations.  Protected by main lock.
+     */
+    final StringBuilder mStringBuilder = new StringBuilder(256);
+    
+    /**
      * Used to control how we initialize the service.
      */
     boolean mStartRunning = false;
@@ -702,6 +711,11 @@
     boolean mSleeping = false;
 
     /**
+     * Set if we are shutting down the system, similar to sleeping.
+     */
+    boolean mShuttingDown = false;
+    
+    /**
      * Set when the system is going to sleep, until we have
      * successfully paused the current activity and released our wake lock.
      * At that point the system is allowed to actually sleep.
@@ -778,6 +792,8 @@
     long mLastCpuTime = 0;
     long mLastWriteTime = 0;
 
+    long mInitialStartTime = 0;
+    
     /**
      * Set to true after the system has finished booting.
      */
@@ -861,7 +877,7 @@
                         return;
                     }
                     AppErrorResult res = (AppErrorResult) data.get("result");
-                    if (!mSleeping) {
+                    if (!mSleeping && !mShuttingDown) {
                         Dialog d = new AppErrorDialog(
                                 mContext, res, proc,
                                 (Integer)data.get("flags"),
@@ -1046,7 +1062,7 @@
 
             ApplicationInfo info =
                 mSelf.mContext.getPackageManager().getApplicationInfo(
-                        "android", PackageManager.GET_SHARED_LIBRARY_FILES);
+                        "android", STOCK_PM_FLAGS);
             synchronized (mSelf) {
                 ProcessRecord app = mSelf.newProcessRecordLocked(
                         mSystemThread.getApplicationThread(), info,
@@ -1270,7 +1286,7 @@
         mBatteryStatsService.getActiveStatistics().writeLocked();
         
         mUsageStatsService = new UsageStatsService( new File(
-                systemDir, "usagestats.bin").toString());
+                systemDir, "usagestats").toString());
 
         mConfiguration.makeDefault();
         mProcessStats.init();
@@ -1635,6 +1651,11 @@
         
         if (r.startTime == 0) {
             r.startTime = SystemClock.uptimeMillis();
+            if (mInitialStartTime == 0) {
+                mInitialStartTime = r.startTime;
+            }
+        } else if (mInitialStartTime == 0) {
+            mInitialStartTime = SystemClock.uptimeMillis();
         }
         
         if (app != null && app.thread != null) {
@@ -1785,7 +1806,8 @@
                 Watchdog.getInstance().processStarted(app, app.processName, pid);
             }
             
-            StringBuilder buf = new StringBuilder(128);
+            StringBuilder buf = mStringBuilder;
+            buf.setLength(0);
             buf.append("Start proc ");
             buf.append(app.processName);
             buf.append(" for ");
@@ -1880,7 +1902,7 @@
 
         // If we are not going to sleep, we want to ensure the device is
         // awake until the next activity is started.
-        if (!mSleeping) {
+        if (!mSleeping && !mShuttingDown) {
             mLaunchingActivity.acquire();
             if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
                 // To be safe, don't allow the wake lock to be held for too long.
@@ -1959,12 +1981,15 @@
             mPausingActivity = null;
         }
 
-        if (!mSleeping) {
+        if (!mSleeping && !mShuttingDown) {
             resumeTopActivityLocked(prev);
         } else {
             if (mGoingToSleep.isHeld()) {
                 mGoingToSleep.release();
             }
+            if (mShuttingDown) {
+                notifyAll();
+            }
         }
         
         if (prev != null) {
@@ -2203,7 +2228,7 @@
             }
             ActivityInfo aInfo =
                 intent.resolveActivityInfo(mContext.getPackageManager(),
-                        PackageManager.GET_SHARED_LIBRARY_FILES);
+                        STOCK_PM_FLAGS);
             if (aInfo != null) {
                 intent.setComponent(new ComponentName(
                         aInfo.applicationInfo.packageName, aInfo.name));
@@ -2230,7 +2255,8 @@
 
         // If we are sleeping, and there is no resumed activity, and the top
         // activity is paused, well that is the state we want.
-        if (mSleeping && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
+        if ((mSleeping || mShuttingDown)
+                && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
             mWindowManager.executeAppTransition();
@@ -2813,7 +2839,6 @@
         HistoryRecord r = new HistoryRecord(this, callerApp, callingUid,
                 intent, resolvedType, aInfo, mConfiguration,
                 resultRecord, resultWho, requestCode, componentSpecified);
-        r.startTime = SystemClock.uptimeMillis();
 
         HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
                 != 0 ? r : null;
@@ -3160,7 +3185,7 @@
                 ActivityThread.getPackageManager().resolveIntent(
                         intent, resolvedType,
                         PackageManager.MATCH_DEFAULT_ONLY
-                        | PackageManager.GET_SHARED_LIBRARY_FILES);
+                        | STOCK_PM_FLAGS);
             aInfo = rInfo != null ? rInfo.activityInfo : null;
         } catch (RemoteException e) {
             aInfo = null;
@@ -3221,8 +3246,7 @@
                 List<ResolveInfo> resolves =
                     ActivityThread.getPackageManager().queryIntentActivities(
                             intent, r.resolvedType,
-                            PackageManager.MATCH_DEFAULT_ONLY
-                            | PackageManager.GET_SHARED_LIBRARY_FILES);
+                            PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
 
                 // Look for the original activity in the list...
                 final int N = resolves != null ? resolves.size() : 0;
@@ -3304,8 +3328,7 @@
             ResolveInfo rInfo =
                 ActivityThread.getPackageManager().resolveIntent(
                         intent, resolvedType,
-                        PackageManager.MATCH_DEFAULT_ONLY
-                        | PackageManager.GET_SHARED_LIBRARY_FILES);
+                        PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
             aInfo = rInfo != null ? rInfo.activityInfo : null;
         } catch (RemoteException e) {
             aInfo = null;
@@ -4095,7 +4118,8 @@
             }
         }
 
-        StringBuilder info = new StringBuilder();
+        StringBuilder info = mStringBuilder;
+        info.setLength(0);
         info.append("ANR (application not responding) in process: ");
         info.append(app.processName);
         if (annotation != null) {
@@ -4517,7 +4541,7 @@
             }
         } else if (mStartingProcesses.size() > 0) {
             app = mStartingProcesses.remove(0);
-            app.pid = pid;
+            app.setPid(pid);
         } else {
             app = null;
         }
@@ -4586,7 +4610,8 @@
                     mWaitForDebugger = mOrigWaitForDebugger;
                 }
             }
-            thread.bindApplication(processName, app.info, providers,
+            thread.bindApplication(processName, app.instrumentationInfo != null
+                    ? app.instrumentationInfo : app.info, providers,
                     app.instrumentationClass, app.instrumentationProfileFile,
                     app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                     mConfiguration, getCommonServicesLocked());
@@ -6629,8 +6654,7 @@
         try {
             providers = ActivityThread.getPackageManager().
                 queryContentProviders(app.processName, app.info.uid,
-                        PackageManager.GET_SHARED_LIBRARY_FILES
-                        | PackageManager.GET_URI_PERMISSION_PATTERNS);
+                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
         } catch (RemoteException ex) {
         }
         if (providers != null) {
@@ -6734,7 +6758,8 @@
             } else {
                 try {
                     cpi = ActivityThread.getPackageManager().
-                        resolveContentProvider(name, PackageManager.GET_URI_PERMISSION_PATTERNS);
+                        resolveContentProvider(name,
+                                STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
                 } catch (RemoteException ex) {
                 }
                 if (cpi == null) {
@@ -6755,7 +6780,7 @@
                             ActivityThread.getPackageManager().
                                 getApplicationInfo(
                                         cpi.applicationInfo.packageName,
-                                        PackageManager.GET_SHARED_LIBRARY_FILES);
+                                        STOCK_PM_FLAGS);
                         if (ai == null) {
                             Log.w(TAG, "No package info for content provider "
                                     + cpi.name);
@@ -7085,8 +7110,45 @@
         }
     }
 
+    public boolean shutdown(int timeout) {
+        if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.SHUTDOWN);
+        }
+        
+        boolean timedout = false;
+        
+        synchronized(this) {
+            mShuttingDown = true;
+            mWindowManager.setEventDispatching(false);
+
+            if (mResumedActivity != null) {
+                pauseIfSleepingLocked();
+                final long endTime = System.currentTimeMillis() + timeout;
+                while (mResumedActivity != null || mPausingActivity != null) {
+                    long delay = endTime - System.currentTimeMillis();
+                    if (delay <= 0) {
+                        Log.w(TAG, "Activity manager shutdown timed out");
+                        timedout = true;
+                        break;
+                    }
+                    try {
+                        this.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+        }
+        
+        mUsageStatsService.shutdown();
+        mBatteryStatsService.shutdown();
+        
+        return timedout;
+    }
+    
     void pauseIfSleepingLocked() {
-        if (mSleeping) {
+        if (mSleeping || mShuttingDown) {
             if (!mGoingToSleep.isHeld()) {
                 mGoingToSleep.acquire();
                 if (mLaunchingActivity.isHeld()) {
@@ -7454,7 +7516,7 @@
             if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
                 ResolveInfo ri = mContext.getPackageManager()
                         .resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
-                                0);
+                                STOCK_PM_FLAGS);
                 CharSequence errorMsg = null;
                 if (ri != null) {
                     ActivityInfo ai = ri.activityInfo;
@@ -7490,7 +7552,7 @@
             if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
                 try {
                     List apps = ActivityThread.getPackageManager().
-                        getPersistentApplications(PackageManager.GET_SHARED_LIBRARY_FILES);
+                        getPersistentApplications(STOCK_PM_FLAGS);
                     if (apps != null) {
                         int N = apps.size();
                         int i;
@@ -7877,24 +7939,24 @@
                 return;
             }
             pw.println("Activities in Current Activity Manager State:");
-            dumpHistoryList(pw, mHistory, "  ", "History");
+            dumpHistoryList(pw, mHistory, "  ", "Hist", true);
             pw.println(" ");
             pw.println("  Running activities (most recent first):");
-            dumpHistoryList(pw, mLRUActivities, "  ", "Running");
+            dumpHistoryList(pw, mLRUActivities, "  ", "Run", false);
             if (mWaitingVisibleActivities.size() > 0) {
                 pw.println(" ");
                 pw.println("  Activities waiting for another to become visible:");
-                dumpHistoryList(pw, mWaitingVisibleActivities, "  ", "Waiting");
+                dumpHistoryList(pw, mWaitingVisibleActivities, "  ", "Wait", false);
             }
             if (mStoppingActivities.size() > 0) {
                 pw.println(" ");
                 pw.println("  Activities waiting to stop:");
-                dumpHistoryList(pw, mStoppingActivities, "  ", "Stopping");
+                dumpHistoryList(pw, mStoppingActivities, "  ", "Stop", false);
             }
             if (mFinishingActivities.size() > 0) {
                 pw.println(" ");
                 pw.println("  Activities waiting to finish:");
-                dumpHistoryList(pw, mFinishingActivities, "  ", "Finishing");
+                dumpHistoryList(pw, mFinishingActivities, "  ", "Fin", false);
             }
 
             pw.println(" ");
@@ -7909,7 +7971,9 @@
 
                 final int N = mRecentTasks.size();
                 for (int i=0; i<N; i++) {
-                    pw.println("  Recent Task #" + i);
+                    TaskRecord tr = mRecentTasks.get(i);
+                    pw.print("  * Recent #"); pw.print(i); pw.print(": ");
+                            pw.println(tr);
                     mRecentTasks.get(i).dump(pw, "    ");
                 }
             }
@@ -7931,8 +7995,9 @@
                         needSep = true;
                     }
                     ProcessRecord r = procs.valueAt(ia);
-                    pw.println((r.persistent ? "  *PERSISTENT* Process [" : "  Process [")
-                            + r.processName + "] UID " + procs.keyAt(ia));
+                    pw.print(r.persistent ? "  *PERS*" : "  *APP*");
+                        pw.print(" UID "); pw.print(procs.keyAt(ia));
+                        pw.print(" "); pw.println(r);
                     r.dump(pw, "    ");
                     if (r.persistent) {
                         numPers++;
@@ -7945,7 +8010,7 @@
                 needSep = true;
                 pw.println("  Running processes (most recent first):");
                 dumpProcessList(pw, mLRUProcesses, "    ",
-                        "Running Norm Proc", "Running PERS Proc", true);
+                        "App ", "PERS", true);
                 needSep = true;
             }
 
@@ -7955,8 +8020,8 @@
                     needSep = true;
                     pw.println("  PID mappings:");
                     for (int i=0; i<mPidsSelfLocked.size(); i++) {
-                        pw.println("    PID #" + mPidsSelfLocked.keyAt(i)
-                                + ": " + mPidsSelfLocked.valueAt(i));
+                        pw.print("    PID #"); pw.print(mPidsSelfLocked.keyAt(i));
+                            pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
                     }
                 }
             }
@@ -7966,8 +8031,8 @@
                 needSep = true;
                 pw.println("  Foreground Processes:");
                 for (int i=0; i<mForegroundProcesses.size(); i++) {
-                    pw.println("    PID #" + mForegroundProcesses.keyAt(i)
-                            + ": " + mForegroundProcesses.valueAt(i));
+                    pw.print("    PID #"); pw.print(mForegroundProcesses.keyAt(i));
+                            pw.print(": "); pw.println(mForegroundProcesses.valueAt(i));
                 }
             }
             
@@ -7976,7 +8041,7 @@
                 needSep = true;
                 pw.println("  Persisent processes that are starting:");
                 dumpProcessList(pw, mPersistentStartingProcesses, "    ",
-                        "Starting Initial Proc", "Restarting PERS Proc", false);
+                        "Starting Norm", "Restarting PERS", false);
             }
 
             if (mStartingProcesses.size() > 0) {
@@ -7984,7 +8049,7 @@
                 needSep = true;
                 pw.println("  Processes that are starting:");
                 dumpProcessList(pw, mStartingProcesses, "    ",
-                        "Starting Norm Proc", "Starting PERS Proc", false);
+                        "Starting Norm", "Starting PERS", false);
             }
 
             if (mRemovedProcesses.size() > 0) {
@@ -7992,7 +8057,7 @@
                 needSep = true;
                 pw.println("  Processes that are being removed:");
                 dumpProcessList(pw, mRemovedProcesses, "    ",
-                        "Removed Norm Proc", "Removed PERS Proc", false);
+                        "Removed Norm", "Removed PERS", false);
             }
             
             if (mProcessesOnHold.size() > 0) {
@@ -8000,7 +8065,7 @@
                 needSep = true;
                 pw.println("  Processes that are on old until the system is ready:");
                 dumpProcessList(pw, mProcessesOnHold, "    ",
-                        "OnHold Norm Proc", "OnHold PERS Proc", false);
+                        "OnHold Norm", "OnHold PERS", false);
             }
 
             if (mProcessCrashTimes.getMap().size() > 0) {
@@ -8013,10 +8078,11 @@
                     SparseArray<Long> uids = procs.getValue();
                     final int N = uids.size();
                     for (int i=0; i<N; i++) {
-                        pw.println("    Process " + procs.getKey()
-                                + " uid " + uids.keyAt(i)
-                                + ": last crashed "
-                                + (now-uids.valueAt(i)) + " ms ago");
+                        pw.print("    Process "); pw.print(procs.getKey());
+                                pw.print(" uid "); pw.print(uids.keyAt(i));
+                                pw.print(": last crashed ");
+                                pw.print((now-uids.valueAt(i)));
+                                pw.println(" ms ago");
                     }
                 }
             }
@@ -8030,9 +8096,10 @@
                     SparseArray<Long> uids = procs.getValue();
                     final int N = uids.size();
                     for (int i=0; i<N; i++) {
-                        pw.println("    Bad process " + procs.getKey()
-                                + " uid " + uids.keyAt(i)
-                                + ": crashed at time " + uids.valueAt(i));
+                        pw.print("    Bad process "); pw.print(procs.getKey());
+                                pw.print(" uid "); pw.print(uids.keyAt(i));
+                                pw.print(": crashed at time ");
+                                pw.println(uids.valueAt(i));
                     }
                 }
             }
@@ -8046,7 +8113,7 @@
                     + " mBooting=" + mBooting
                     + " mBooted=" + mBooted
                     + " mFactoryTest=" + mFactoryTest);
-            pw.println("  mSleeping=" + mSleeping);
+            pw.println("  mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
             pw.println("  mGoingToSleep=" + mGoingToSleep);
             pw.println("  mLaunchingActivity=" + mLaunchingActivity);
             pw.println("  mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp
@@ -8131,14 +8198,14 @@
                 Iterator it = mRegisteredReceivers.values().iterator();
                 while (it.hasNext()) {
                     ReceiverList r = (ReceiverList)it.next();
-                    pw.println("  Receiver " + r.receiver);
+                    pw.print("  * "); pw.println(r);
                     r.dump(pw, "    ");
                 }
             }
 
             pw.println(" ");
             pw.println("Receiver Resolver Table:");
-            mReceiverResolver.dump(new PrintWriterPrinter(pw), "  ");
+            mReceiverResolver.dump(pw, "  ");
             
             if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
                     || mPendingBroadcast != null) {
@@ -8172,13 +8239,23 @@
             if (mStickyBroadcasts != null) {
                 pw.println(" ");
                 pw.println("  Sticky broadcasts:");
+                StringBuilder sb = new StringBuilder(128);
                 for (Map.Entry<String, ArrayList<Intent>> ent
                         : mStickyBroadcasts.entrySet()) {
-                    pw.println("  Sticky action " + ent.getKey() + ":");
+                    pw.print("  * Sticky action "); pw.print(ent.getKey());
+                            pw.println(":");
                     ArrayList<Intent> intents = ent.getValue();
                     final int N = intents.size();
                     for (int i=0; i<N; i++) {
-                        pw.println("    " + intents.get(i));
+                        sb.setLength(0);
+                        sb.append("    Intent: ");
+                        intents.get(i).toShortString(sb, true, false);
+                        pw.println(sb.toString());
+                        Bundle bundle = intents.get(i).getExtras();
+                        if (bundle != null) {
+                            pw.print("      ");
+                            pw.println(bundle.toString());
+                        }
                     }
                 }
             }
@@ -8209,7 +8286,7 @@
                 Iterator<ServiceRecord> it = mServices.values().iterator();
                 while (it.hasNext()) {
                     ServiceRecord r = it.next();
-                    pw.println("  Service " + r.shortName);
+                    pw.print("  * "); pw.println(r);
                     r.dump(pw, "    ");
                 }
                 needSep = true;
@@ -8220,7 +8297,7 @@
                 pw.println("  Pending services:");
                 for (int i=0; i<mPendingServices.size(); i++) {
                     ServiceRecord r = mPendingServices.get(i);
-                    pw.println("  Pending Service " + r.shortName);
+                    pw.print("  * Pending "); pw.println(r);
                     r.dump(pw, "    ");
                 }
                 needSep = true;
@@ -8231,7 +8308,7 @@
                 pw.println("  Restarting services:");
                 for (int i=0; i<mRestartingServices.size(); i++) {
                     ServiceRecord r = mRestartingServices.get(i);
-                    pw.println("  Restarting Service " + r.shortName);
+                    pw.print("  * Restarting "); pw.println(r);
                     r.dump(pw, "    ");
                 }
                 needSep = true;
@@ -8242,7 +8319,7 @@
                 pw.println("  Stopping services:");
                 for (int i=0; i<mStoppingServices.size(); i++) {
                     ServiceRecord r = mStoppingServices.get(i);
-                    pw.println("  Stopping Service " + r.shortName);
+                    pw.print("  * Stopping "); pw.println(r);
                     r.dump(pw, "    ");
                 }
                 needSep = true;
@@ -8255,8 +8332,7 @@
                         = mServiceConnections.values().iterator();
                 while (it.hasNext()) {
                     ConnectionRecord r = it.next();
-                    pw.println("  " + r.binding.service.shortName
-                          + " -> " + r.conn.asBinder());
+                    pw.print("  * "); pw.println(r);
                     r.dump(pw, "    ");
                 }
             }
@@ -8279,18 +8355,6 @@
 
             boolean needSep = false;
 
-            if (mProvidersByName.size() > 0) {
-                pw.println("  Published content providers (by name):");
-                Iterator it = mProvidersByName.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry e = (Map.Entry)it.next();
-                    ContentProviderRecord r = (ContentProviderRecord)e.getValue();
-                    pw.println("  Provider " + (String)e.getKey());
-                    r.dump(pw, "    ");
-                }
-                needSep = true;
-            }
-
             if (mProvidersByClass.size() > 0) {
                 if (needSep) pw.println(" ");
                 pw.println("  Published content providers (by class):");
@@ -8298,31 +8362,48 @@
                 while (it.hasNext()) {
                     Map.Entry e = (Map.Entry)it.next();
                     ContentProviderRecord r = (ContentProviderRecord)e.getValue();
-                    pw.println("  Provider " + (String)e.getKey());
+                    pw.print("  * "); pw.println(r);
                     r.dump(pw, "    ");
                 }
                 needSep = true;
             }
 
+            if (mProvidersByName.size() > 0) {
+                pw.println(" ");
+                pw.println("  Authority to provider mappings:");
+                Iterator it = mProvidersByName.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry e = (Map.Entry)it.next();
+                    ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+                    pw.print("  "); pw.print(e.getKey()); pw.print(": ");
+                            pw.println(r);
+                }
+                needSep = true;
+            }
+
             if (mLaunchingProviders.size() > 0) {
                 if (needSep) pw.println(" ");
                 pw.println("  Launching content providers:");
                 for (int i=mLaunchingProviders.size()-1; i>=0; i--) {
-                    pw.println("  Provider #" + i + ":");
-                    ((ContentProviderRecord)mLaunchingProviders.get(i)).dump(pw, "    ");
+                    pw.print("  Launching #"); pw.print(i); pw.print(": ");
+                            pw.println(mLaunchingProviders.get(i));
                 }
                 needSep = true;
             }
 
-            pw.println();
-            pw.println("Granted Uri Permissions:");
-            for (int i=0; i<mGrantedUriPermissions.size(); i++) {
-                int uid = mGrantedUriPermissions.keyAt(i);
-                HashMap<Uri, UriPermission> perms
-                        = mGrantedUriPermissions.valueAt(i);
-                pw.println("  Uris granted to uid " + uid + ":");
-                for (UriPermission perm : perms.values()) {
-                    perm.dump(pw, "    ");
+            if (mGrantedUriPermissions.size() > 0) {
+                pw.println();
+                pw.println("Granted Uri Permissions:");
+                for (int i=0; i<mGrantedUriPermissions.size(); i++) {
+                    int uid = mGrantedUriPermissions.keyAt(i);
+                    HashMap<Uri, UriPermission> perms
+                            = mGrantedUriPermissions.valueAt(i);
+                    pw.print("  * UID "); pw.print(uid);
+                            pw.println(" holds:");
+                    for (UriPermission perm : perms.values()) {
+                        pw.print("    "); pw.println(perm);
+                        perm.dump(pw, "      ");
+                    }
                 }
             }
         }
@@ -8340,7 +8421,7 @@
                 return;
             }
 
-            pw.println("Intent Senders in Current Activity Manager State:");
+            pw.println("Pending Intents in Current Activity Manager State:");
 
             if (this.mIntentSenderRecords.size() > 0) {
                 Iterator<WeakReference<PendingIntentRecord>> it
@@ -8349,10 +8430,10 @@
                     WeakReference<PendingIntentRecord> ref = it.next();
                     PendingIntentRecord rec = ref != null ? ref.get(): null;
                     if (rec != null) {
-                        pw.println("  IntentSender " + rec);
+                        pw.print("  * "); pw.println(rec);
                         rec.dump(pw, "    ");
                     } else {
-                        pw.println("  IntentSender " + ref);
+                        pw.print("  * "); pw.print(ref);
                     }
                 }
             }
@@ -8360,16 +8441,26 @@
     }
 
     private static final void dumpHistoryList(PrintWriter pw, List list,
-            String prefix, String label) {
+            String prefix, String label, boolean complete) {
         TaskRecord lastTask = null;
         for (int i=list.size()-1; i>=0; i--) {
             HistoryRecord r = (HistoryRecord)list.get(i);
+            final boolean full = complete || !r.inHistory;
             if (lastTask != r.task) {
                 lastTask = r.task;
-                lastTask.dump(pw, prefix + "  ");
+                pw.print(prefix);
+                pw.print(full ? "* " : "  ");
+                pw.println(lastTask);
+                if (full) {
+                    lastTask.dump(pw, prefix + "  ");
+                }
             }
-            pw.println(prefix + "    " + label + " #" + i + ":");
-            r.dump(pw, prefix + "      ");
+            pw.print(prefix); pw.print(full ? "  * " : "    "); pw.print(label);
+            pw.print(" #"); pw.print(i); pw.print(": ");
+            pw.println(r);
+            if (full) {
+                r.dump(pw, prefix + "      ");
+            }
         }
     }
 
@@ -8401,7 +8492,7 @@
 
     private static final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, List list, String prefix, String[] args) {
-        final boolean isCheckinRequest = scanArgs(args, "-c");
+        final boolean isCheckinRequest = scanArgs(args, "--checkin");
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
         
@@ -8738,7 +8829,7 @@
                 mPidsSelfLocked.remove(app.pid);
                 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             }
-            app.pid = 0;
+            app.setPid(0);
         }
     }
 
@@ -8876,7 +8967,7 @@
             try {
                 ResolveInfo rInfo =
                     ActivityThread.getPackageManager().resolveService(
-                            service, resolvedType, PackageManager.GET_SHARED_LIBRARY_FILES);
+                            service, resolvedType, STOCK_PM_FLAGS);
                 ServiceInfo sInfo =
                     rInfo != null ? rInfo.serviceInfo : null;
                 if (sInfo == null) {
@@ -10094,7 +10185,7 @@
             if (intent.getComponent() != null) {
                 // Broadcast is going to one specific receiver class...
                 ActivityInfo ai = ActivityThread.getPackageManager().
-                    getReceiverInfo(intent.getComponent(), 0);
+                    getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);
                 if (ai != null) {
                     receivers = new ArrayList();
                     ResolveInfo ri = new ResolveInfo();
@@ -10107,7 +10198,7 @@
                          == 0) {
                     receivers =
                         ActivityThread.getPackageManager().queryIntentReceivers(
-                                intent, resolvedType, PackageManager.GET_SHARED_LIBRARY_FILES);
+                                intent, resolvedType, STOCK_PM_FLAGS);
                 }
                 registeredReceivers = mReceiverResolver.queryIntent(resolver,
                         intent, resolvedType, false);
@@ -10844,9 +10935,9 @@
             ApplicationInfo ai = null;
             try {
                 ii = mContext.getPackageManager().getInstrumentationInfo(
-                    className, 0);
+                    className, STOCK_PM_FLAGS);
                 ai = mContext.getPackageManager().getApplicationInfo(
-                    ii.targetPackage, PackageManager.GET_SHARED_LIBRARY_FILES);
+                    ii.targetPackage, STOCK_PM_FLAGS);
             } catch (PackageManager.NameNotFoundException e) {
             }
             if (ii == null) {
@@ -10878,6 +10969,7 @@
             uninstallPackageLocked(ii.targetPackage, -1, true);
             ProcessRecord app = addAppLocked(ai);
             app.instrumentationClass = className;
+            app.instrumentationInfo = ai;
             app.instrumentationProfileFile = profileFile;
             app.instrumentationArguments = arguments;
             app.instrumentationWatcher = watcher;
@@ -10925,6 +11017,7 @@
         }
         app.instrumentationWatcher = null;
         app.instrumentationClass = null;
+        app.instrumentationInfo = null;
         app.instrumentationProfileFile = null;
         app.instrumentationArguments = null;
 
diff --git a/services/java/com/android/server/am/AppBindRecord.java b/services/java/com/android/server/am/AppBindRecord.java
index ce6f6dc..9c57360 100644
--- a/services/java/com/android/server/am/AppBindRecord.java
+++ b/services/java/com/android/server/am/AppBindRecord.java
@@ -32,9 +32,12 @@
                                     // All ConnectionRecord for this client.
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
         pw.println(prefix + "service=" + service);
         pw.println(prefix + "client=" + client);
+        dumpInIntentBind(pw, prefix);
+    }
+
+    void dumpInIntentBind(PrintWriter pw, String prefix) {
         if (connections.size() > 0) {
             pw.println(prefix + "Per-process Connections:");
             Iterator<ConnectionRecord> it = connections.iterator();
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 9ad994c..0387be5 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -25,8 +25,7 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.ServiceManager;
-import android.telephony.TelephonyManager;
-import android.util.PrintWriterPrinter;
+import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -50,6 +49,13 @@
         ServiceManager.addService("batteryinfo", asBinder());
     }
     
+    public void shutdown() {
+        Log.w("BatteryStats", "Writing battery stats before shutdown...");
+        synchronized (mStats) {
+            mStats.writeLocked();
+        }
+    }
+    
     public static IBatteryStats getService() {
         if (sService != null) {
             return sService;
@@ -255,6 +261,20 @@
         }
     }
 
+    public void noteWifiMulticastEnabled(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWifiMulticastEnabledLocked(uid);
+        }
+    }
+
+    public void noteWifiMulticastDisabled(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWifiMulticastDisabledLocked(uid);
+        }
+    }
+
     public boolean isOnBattery() {
         return mStats.isOnBattery();
     }
@@ -264,6 +284,11 @@
         mStats.setOnBattery(onBattery, level);
     }
     
+    public void recordCurrentLevel(int level) {
+        enforceCallingPermission();
+        mStats.recordCurrentLevel(level);
+    }
+    
     public long getAwakeTimeBattery() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
@@ -290,14 +315,14 @@
             boolean isCheckin = false;
             if (args != null) {
                 for (String arg : args) {
-                    if ("-c".equals(arg)) {
+                    if ("--checkin".equals(arg)) {
                         isCheckin = true;
                         break;
                     }
                 }
             }
             if (isCheckin) mStats.dumpCheckinLocked(pw, args);
-            else mStats.dumpLocked(new PrintWriterPrinter(pw));
+            else mStats.dumpLocked(pw);
         }
     }
 }
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index cd7f720..0eeb393 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -18,6 +18,7 @@
 
 import android.content.IntentFilter;
 import android.util.PrintWriterPrinter;
+import android.util.Printer;
 
 import java.io.PrintWriter;
 
@@ -33,19 +34,25 @@
         requiredPermission = _requiredPermission;
     }
     
-    public void dumpLocal(PrintWriter pw, String prefix) {
-        super.dump(new PrintWriterPrinter(pw), prefix);
-    }
-    
     public void dump(PrintWriter pw, String prefix) {
-        dumpLocal(pw, prefix);
-        pw.println(prefix + "requiredPermission=" + requiredPermission);
+        dumpInReceiverList(pw, new PrintWriterPrinter(pw), prefix);
         receiverList.dumpLocal(pw, prefix);
     }
     
+    public void dumpInReceiverList(PrintWriter pw, Printer pr, String prefix) {
+        super.dump(pr, prefix);
+        if (requiredPermission != null) {
+            pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
+        }
+    }
+    
     public String toString() {
-        return "BroadcastFilter{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + receiverList + "}";
+        StringBuilder sb = new StringBuilder();
+        sb.append("BroadcastFilter{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(receiverList);
+        sb.append('}');
+        return sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index 41a783f..b3343dd 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -28,11 +28,13 @@
     final HistoryRecord activity;   // If non-null, the owning activity.
     final IServiceConnection conn;  // The client connection.
     final int flags;                // Binding options.
-
+    String stringName;              // Caching of toString.
+    
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
         pw.println(prefix + "binding=" + binding);
-        pw.println(prefix + "activity=" + activity);
+        if (activity != null) {
+            pw.println(prefix + "activity=" + activity);
+        }
         pw.println(prefix + "conn=" + conn.asBinder()
                 + " flags=0x" + Integer.toHexString(flags));
     }
@@ -46,9 +48,17 @@
     }
 
     public String toString() {
-        return "ConnectionRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + binding.service.shortName
-            + ":@" + Integer.toHexString(System.identityHashCode(conn.asBinder())) + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ConnectionRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(binding.service.shortName);
+        sb.append(":@");
+        sb.append(Integer.toHexString(System.identityHashCode(conn.asBinder())));
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 9f37c14..c764635 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -32,7 +32,8 @@
     int externals;     // number of non-framework processes supported by this provider
     ProcessRecord app; // if non-null, hosting application
     ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
-
+    String stringName;
+    
     public ContentProviderRecord(ProviderInfo _info, ApplicationInfo ai) {
         super(_info);
         uid = ai.uid;
@@ -53,24 +54,39 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "package=" + info.applicationInfo.packageName
-              + " process=" + info.processName);
-        pw.println(prefix + "app=" + app);
-        pw.println(prefix + "launchingApp=" + launchingApp);
-        pw.println(prefix + "provider=" + provider);
-        pw.println(prefix + "name=" + info.authority);
-        pw.println(prefix + "isSyncable=" + info.isSyncable);
-        pw.println(prefix + "multiprocess=" + info.multiprocess
-              + " initOrder=" + info.initOrder
-              + " uid=" + uid);
-        pw.println(prefix + "clients=" + clients);
-        pw.println(prefix + "externals=" + externals);
+        pw.print(prefix); pw.print("package=");
+                pw.print(info.applicationInfo.packageName);
+                pw.print("process="); pw.println(info.processName);
+        pw.print(prefix); pw.print("app="); pw.println(app);
+        if (launchingApp != null) {
+            pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
+        }
+        pw.print(prefix); pw.print("uid="); pw.print(uid);
+                pw.print(" provider="); pw.println(provider);
+        pw.print(prefix); pw.print("name="); pw.println(info.authority);
+        if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
+            pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
+                    pw.print("multiprocess="); pw.print(info.multiprocess);
+                    pw.print(" initOrder="); pw.println(info.initOrder);
+        }
+        if (clients.size() > 0) {
+            pw.print(prefix); pw.print("clients="); pw.println(clients);
+        }
+        if (externals != 0) {
+            pw.print(prefix); pw.print("externals="); pw.println(externals);
+        }
     }
 
     public String toString() {
-        return "ContentProviderRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + info.name + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ContentProviderRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(info.name);
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 0f62471..1488791 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -100,46 +100,74 @@
     boolean hasBeenLaunched;// has this activity ever been launched?
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
 
+    String stringName;      // for caching of toString().
+    
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "packageName=" + packageName
-              + " processName=" + processName);
-        pw.println(prefix + "launchedFromUid=" + launchedFromUid
-                + " app=" + app);
-        pw.println(prefix + intent);
-        pw.println(prefix + "frontOfTask=" + frontOfTask + " task=" + task);
-        pw.println(prefix + "taskAffinity=" + taskAffinity);
-        pw.println(prefix + "realActivity=" + realActivity);
-        pw.println(prefix + "dir=" + baseDir + " res=" + resDir + " data=" + dataDir);
-        pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
-                + " icon=0x" + Integer.toHexString(icon)
-                + " theme=0x" + Integer.toHexString(theme));
-        pw.println(prefix + "stateNotNeeded=" + stateNotNeeded
-                + " componentSpecified=" + componentSpecified
-                + " isHomeActivity=" + isHomeActivity);
-        pw.println(prefix + "configuration=" + configuration);
-        pw.println(prefix + "resultTo=" + resultTo
-              + " resultWho=" + resultWho + " resultCode=" + requestCode);
-        pw.println(prefix + "results=" + results);
-        pw.println(prefix + "pendingResults=" + pendingResults);
-        pw.println(prefix + "readUriPermissions=" + readUriPermissions);
-        pw.println(prefix + "writeUriPermissions=" + writeUriPermissions);
-        pw.println(prefix + "launchFailed=" + launchFailed
-              + " haveState=" + haveState + " icicle=" + icicle);
-        pw.println(prefix + "state=" + state
-              + " stopped=" + stopped + " finishing=" + finishing);
-        pw.println(prefix + "keysPaused=" + keysPaused
-              + " inHistory=" + inHistory + " persistent=" + persistent
-              + " launchMode=" + launchMode);
-        pw.println(prefix + "fullscreen=" + fullscreen
-              + " visible=" + visible
-              + " frozenBeforeDestroy=" + frozenBeforeDestroy
-              + " thumbnailNeeded=" + thumbnailNeeded + " idle=" + idle);
-        pw.println(prefix + "waitingVisible=" + waitingVisible
-              + " nowVisible=" + nowVisible);
-        pw.println(prefix + "configDestroy=" + configDestroy
-                + " configChangeFlags=" + Integer.toHexString(configChangeFlags));
-        pw.println(prefix + "connections=" + connections);
+        pw.print(prefix); pw.print("packageName="); pw.print(packageName);
+                pw.print(" processName="); pw.println(processName);
+        pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+                pw.print(" app="); pw.println(app);
+        pw.print(prefix); pw.println(intent);
+        pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
+                pw.print(" task="); pw.println(task);
+        pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
+        pw.print(prefix); pw.print("realActivity=");
+                pw.println(realActivity.flattenToShortString());
+        pw.print(prefix); pw.print("base="); pw.print(baseDir);
+                if (!resDir.equals(baseDir)) pw.print(" res="); pw.print(resDir);
+                pw.print(" data="); pw.println(dataDir);
+        pw.print(prefix); pw.print("labelRes=0x");
+                pw.print(Integer.toHexString(labelRes));
+                pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
+                pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
+        pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
+                pw.print(" componentSpecified="); pw.print(componentSpecified);
+                pw.print(" isHomeActivity="); pw.println(isHomeActivity);
+        pw.print(prefix); pw.print("configuration="); pw.println(configuration);
+        if (resultTo != null || resultWho != null) {
+            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+                    pw.print(" resultWho="); pw.print(resultWho);
+                    pw.print(" resultCode="); pw.println(requestCode);
+        }
+        if (results != null) {
+            pw.print(prefix); pw.print("results="); pw.println(results);
+        }
+        if (pendingResults != null) {
+            pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults);
+        }
+        if (readUriPermissions != null) {
+            pw.print(prefix); pw.print("readUriPermissions="); pw.println(readUriPermissions);
+        }
+        if (writeUriPermissions != null) {
+            pw.print(prefix); pw.print("writeUriPermissions="); pw.println(writeUriPermissions);
+        }
+        pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
+                pw.print(" haveState="); pw.print(haveState);
+                pw.print(" icicle="); pw.println(icicle);
+        pw.print(prefix); pw.print("state="); pw.print(state);
+                pw.print(" stopped="); pw.print(stopped);
+                pw.print(" finishing="); pw.println(finishing);
+        pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
+                pw.print(" inHistory="); pw.print(inHistory);
+                pw.print(" persistent="); pw.print(persistent);
+                pw.print(" launchMode="); pw.println(launchMode);
+        pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
+                pw.print(" visible="); pw.print(visible);
+                pw.print(" frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
+                pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
+                pw.print(" idle="); pw.println(idle);
+        if (waitingVisible || nowVisible) {
+            pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
+                    pw.print(" nowVisible="); pw.println(nowVisible);
+        }
+        if (configDestroy || configChangeFlags != 0) {
+            pw.print(prefix); pw.print("configDestroy="); pw.print(configDestroy);
+                    pw.print(" configChangeFlags=");
+                    pw.println(Integer.toHexString(configChangeFlags));
+        }
+        if (connections != null) {
+            pw.print(prefix); pw.print("connections="); pw.println(connections);
+        }
     }
 
     HistoryRecord(ActivityManagerService _service, ProcessRecord _caller,
@@ -335,15 +363,31 @@
     
     public void windowsVisible() {
         synchronized(service) {
-            if (ActivityManagerService.SHOW_ACTIVITY_START_TIME
-                    && startTime != 0) {
-                long time = SystemClock.uptimeMillis() - startTime;
-                EventLog.writeEvent(ActivityManagerService.LOG_ACTIVITY_LAUNCH_TIME,
-                        System.identityHashCode(this), shortComponentName, time);
-                Log.i(ActivityManagerService.TAG, "Displayed activity "
-                        + shortComponentName
-                        + ": " + time + " ms");
+            if (startTime != 0) {
+                final long curTime = SystemClock.uptimeMillis();
+                final long thisTime = curTime - startTime;
+                final long totalTime = service.mInitialStartTime != 0
+                        ? (curTime - service.mInitialStartTime) : thisTime;
+                if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
+                    EventLog.writeEvent(ActivityManagerService.LOG_ACTIVITY_LAUNCH_TIME,
+                            System.identityHashCode(this), shortComponentName,
+                            thisTime, totalTime);
+                    StringBuilder sb = service.mStringBuilder;
+                    sb.setLength(0);
+                    sb.append("Displayed activity ");
+                    sb.append(shortComponentName);
+                    sb.append(": ");
+                    sb.append(thisTime);
+                    sb.append(" ms (total ");
+                    sb.append(totalTime);
+                    sb.append(" ms)");
+                    Log.i(ActivityManagerService.TAG, sb.toString());
+                }
+                if (totalTime > 0) {
+                    service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
+                }
                 startTime = 0;
+                service.mInitialStartTime = 0;
             }
             if (ActivityManagerService.DEBUG_SWITCH) Log.v(
                     ActivityManagerService.TAG, "windowsVisible(): " + this);
@@ -453,8 +497,15 @@
     
     
     public String toString() {
-        return "HistoryRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + intent.getComponent().toShortString() + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("HistoryRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(intent.getComponent().flattenToShortString());
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java
index 24c3943..3a5ca66 100644
--- a/services/java/com/android/server/am/IntentBindRecord.java
+++ b/services/java/com/android/server/am/IntentBindRecord.java
@@ -45,22 +45,30 @@
     /** Set when the service's onUnbind() has asked to be told about new clients. */
     boolean doRebind;
     
+    String stringName;      // caching of toString
+    
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "service=" + service);
-        pw.println(prefix + "intent=" + intent.getIntent());
-        pw.println(prefix + "binder=" + binder
-                + " requested=" + requested
-                + " received=" + received
-                + " hasBound=" + hasBound
-                + " doRebind=" + doRebind);
+        pw.print(prefix); pw.print("service="); pw.println(service);
+        dumpInService(pw, prefix);
+    }
+
+    void dumpInService(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("intent={");
+                pw.print(intent.getIntent().toShortString(true, false));
+                pw.println('}');
+        pw.print(prefix); pw.print("binder="); pw.println(binder);
+        pw.print(prefix); pw.print("requested="); pw.print(requested);
+                pw.print(" received="); pw.print(received);
+                pw.print(" hasBound="); pw.print(hasBound);
+                pw.print(" doRebind="); pw.println(doRebind);
         if (apps.size() > 0) {
-            pw.println(prefix + "Application Bindings:");
             Iterator<AppBindRecord> it = apps.values().iterator();
             while (it.hasNext()) {
                 AppBindRecord a = it.next();
-                pw.println(prefix + "Client " + a.client);
-                a.dump(pw, prefix + "  ");
+                pw.print(prefix); pw.print("* Client AppBindRecord{");
+                        pw.print(Integer.toHexString(System.identityHashCode(a)));
+                        pw.print(' '); pw.print(a.client); pw.println('}');
+                a.dumpInIntentBind(pw, prefix + "  ");
             }
         }
     }
@@ -71,9 +79,19 @@
     }
 
     public String toString() {
-        return "IntentBindRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + service.name.toShortString()
-            + ":" + intent + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("IntentBindRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(service.shortName);
+        sb.append(':');
+        if (intent != null) {
+            intent.getIntent().toShortString(sb, false, false);
+        }
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index b18aaf7..4381392 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -36,6 +36,8 @@
     boolean sent = false;
     boolean canceled = false;
 
+    String stringName;
+    
     final static class Key {
         final int type;
         final String packageName;
@@ -142,7 +144,7 @@
         
         public String toString() {
             return "Key{" + typeName() + " pkg=" + packageName
-                + " intent=" + requestIntent + " flags=0x"
+                + " intent=" + requestIntent.toShortString(true, false) + " flags=0x"
                 + Integer.toHexString(flags) + "}";
         }
         
@@ -260,19 +262,38 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "packageName=" + key.packageName
-                + " type=" + key.typeName()
-                + " flags=0x" + Integer.toHexString(key.flags));
-        pw.println(prefix + "activity=" + key.activity + " who=" + key.who);
-        pw.println(prefix + "requestCode=" + key.requestCode
-                + " requestResolvedType=" + key.requestResolvedType);
-        pw.println(prefix + "requestIntent=" + key.requestIntent);
-        pw.println(prefix + "sent=" + sent + " canceled=" + canceled);
+        pw.print(prefix); pw.print("uid="); pw.print(uid);
+                pw.print(" packageName="); pw.print(key.packageName);
+                pw.print(" type="); pw.print(key.typeName());
+                pw.print(" flags=0x"); pw.println(Integer.toHexString(key.flags));
+        if (key.activity != null || key.who != null) {
+            pw.print(prefix); pw.print("activity="); pw.print(key.activity);
+                    pw.print(" who="); pw.println(key.who);
+        }
+        if (key.requestCode != 0 || key.requestResolvedType != null) {
+            pw.print(prefix); pw.print("requestCode="); pw.print(key.requestCode);
+                    pw.print(" requestResolvedType="); pw.println(key.requestResolvedType);
+        }
+        pw.print(prefix); pw.print("requestIntent=");
+                pw.println(key.requestIntent.toShortString(true, true));
+        if (sent || canceled) {
+            pw.print(prefix); pw.print("sent="); pw.print(sent);
+                    pw.print(" canceled="); pw.println(canceled);
+        }
     }
 
     public String toString() {
-        return "IntentSenderRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + key.packageName + " " + key.typeName() + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("PendingIntentRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(key.packageName);
+        sb.append(' ');
+        sb.append(key.typeName());
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index a1320df..68aebc3 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.PrintWriterPrinter;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -62,6 +63,7 @@
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
     int adjSeq;                 // Sequence id for identifying repeated trav
     ComponentName instrumentationClass;// class installed to instrument app
+    ApplicationInfo instrumentationInfo; // the application being instrumented
     String instrumentationProfileFile; // where to save profiling
     IInstrumentationWatcher instrumentationWatcher; // who is waiting
     Bundle instrumentationArguments;// as given to us
@@ -98,44 +100,86 @@
     boolean waitedForDebugger;  // has process show wait for debugger dialog?
     Dialog waitDialog;          // current wait for debugger dialog
     
+    String stringName;          // caching of toString() result.
+    
     // These reports are generated & stored when an app gets into an error condition.
     // They will be "null" when all is OK.
     ActivityManager.ProcessErrorStateInfo crashingReport;
     ActivityManager.ProcessErrorStateInfo notRespondingReport;
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "class=" + info.className);
-        pw.println(prefix+"manageSpaceActivityName="+info.manageSpaceActivityName);
-        pw.println(prefix + "dir=" + info.sourceDir + " publicDir=" + info.publicSourceDir 
-              + " data=" + info.dataDir);
-        pw.println(prefix + "packageList=" + pkgList);
-        pw.println(prefix + "instrumentationClass=" + instrumentationClass
-              + " instrumentationProfileFile=" + instrumentationProfileFile);
-        pw.println(prefix + "instrumentationArguments=" + instrumentationArguments);
-        pw.println(prefix + "thread=" + thread + " curReceiver=" + curReceiver);
-        pw.println(prefix + "pid=" + pid + " starting=" + starting
-                + " lastPss=" + lastPss);
-        pw.println(prefix + "maxAdj=" + maxAdj + " hiddenAdj=" + hiddenAdj
-                + " curRawAdj=" + curRawAdj + " setRawAdj=" + setRawAdj
-                + " curAdj=" + curAdj + " setAdj=" + setAdj);
-        pw.println(prefix + "isForeground=" + isForeground
-                + " setIsForeground=" + setIsForeground
-                + " foregroundServices=" + foregroundServices
-                + " forcingToForeground=" + forcingToForeground);
-        pw.println(prefix + "persistent=" + persistent + " removed=" + removed
-                + " persistentActivities=" + persistentActivities);
-        pw.println(prefix + "debugging=" + debugging
-                + " crashing=" + crashing + " " + crashDialog
-                + " notResponding=" + notResponding + " " + anrDialog
-                + " bad=" + bad);
-        pw.println(prefix + "activities=" + activities);
-        pw.println(prefix + "services=" + services);
-        pw.println(prefix + "executingServices=" + executingServices);
-        pw.println(prefix + "connections=" + connections);
-        pw.println(prefix + "pubProviders=" + pubProviders);
-        pw.println(prefix + "conProviders=" + conProviders);
-        pw.println(prefix + "receivers=" + receivers);
+        if (info.className != null) {
+            pw.print(prefix); pw.print("class="); pw.println(info.className);
+        }
+        if (info.manageSpaceActivityName != null) {
+            pw.print(prefix); pw.print("manageSpaceActivityName=");
+            pw.println(info.manageSpaceActivityName);
+        }
+        pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir);
+                pw.print(" publicDir="); pw.print(info.publicSourceDir);
+                pw.print(" data="); pw.println(info.dataDir);
+        pw.print(prefix); pw.print("packageList="); pw.println(pkgList);
+        if (instrumentationClass != null || instrumentationProfileFile != null
+                || instrumentationArguments != null) {
+            pw.print(prefix); pw.print("instrumentationClass=");
+                    pw.print(instrumentationClass);
+                    pw.print(" instrumentationProfileFile=");
+                    pw.println(instrumentationProfileFile);
+            pw.print(prefix); pw.print("instrumentationArguments=");
+                    pw.println(instrumentationArguments);
+            pw.print(prefix); pw.print("instrumentationInfo=");
+                    pw.println(instrumentationInfo);
+            if (instrumentationInfo != null) {
+                instrumentationInfo.dump(new PrintWriterPrinter(pw), prefix + "  ");
+            }
+        }
+        pw.print(prefix); pw.print("thread="); pw.print(thread);
+                pw.print(" curReceiver="); pw.println(curReceiver);
+        pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
+                pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
+        pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
+                pw.print(" hidden="); pw.print(hiddenAdj);
+                pw.print(" curRaw="); pw.print(curRawAdj);
+                pw.print(" setRaw="); pw.print(setRawAdj);
+                pw.print(" cur="); pw.print(curAdj);
+                pw.print(" set="); pw.println(setAdj);
+        pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
+                pw.print(" setIsForeground="); pw.print(setIsForeground);
+                pw.print(" foregroundServices="); pw.print(foregroundServices);
+                pw.print(" forcingToForeground="); pw.println(forcingToForeground);
+        pw.print(prefix); pw.print("persistent="); pw.print(persistent);
+                pw.print(" removed="); pw.print(removed);
+                pw.print(" persistentActivities="); pw.println(persistentActivities);
+        if (debugging || crashing || crashDialog != null || notResponding
+                || anrDialog != null || bad) {
+            pw.print(prefix); pw.print("debugging="); pw.print(debugging);
+                    pw.print(" crashing="); pw.print(crashing);
+                    pw.print(" "); pw.print(crashDialog);
+                    pw.print(" notResponding="); pw.print(notResponding);
+                    pw.print(" " ); pw.print(anrDialog);
+                    pw.print(" bad="); pw.println(bad);
+        }
+        if (activities.size() > 0) {
+            pw.print(prefix); pw.print("activities="); pw.println(activities);
+        }
+        if (services.size() > 0) {
+            pw.print(prefix); pw.print("services="); pw.println(services);
+        }
+        if (executingServices.size() > 0) {
+            pw.print(prefix); pw.print("executingServices="); pw.println(executingServices);
+        }
+        if (connections.size() > 0) {
+            pw.print(prefix); pw.print("connections="); pw.println(connections);
+        }
+        if (pubProviders.size() > 0) {
+            pw.print(prefix); pw.print("pubProviders="); pw.println(pubProviders);
+        }
+        if (conProviders.size() > 0) {
+            pw.print(prefix); pw.print("conProviders="); pw.println(conProviders);
+        }
+        if (receivers.size() > 0) {
+            pw.print(prefix); pw.print("receivers="); pw.println(receivers);
+        }
     }
     
     ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
@@ -154,6 +198,11 @@
         persistentActivities = 0;
     }
 
+    public void setPid(int _pid) {
+        pid = _pid;
+        stringName = null;
+    }
+    
     /**
      * This method returns true if any of the activities within the process record are interesting
      * to the user. See HistoryRecord.isInterestingToUserLocked()
@@ -188,9 +237,20 @@
     }
     
     public String toString() {
-        return "ProcessRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + pid + ":" + processName + "/" + info.uid + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ProcessRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(pid);
+        sb.append(':');
+        sb.append(processName);
+        sb.append('/');
+        sb.append(info.uid);
+        sb.append('}');
+        return stringName = sb.toString();
     }
     
     /*
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 6ac527b..0facefc 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -18,9 +18,12 @@
 
 import android.app.IIntentReceiver;
 import android.content.Intent;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -39,6 +42,8 @@
     BroadcastRecord curBroadcast = null;
     boolean linkedToDeath = false;
 
+    String stringName;
+    
     ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
             int _pid, int _uid, IIntentReceiver _receiver) {
         owner = _owner;
@@ -62,31 +67,45 @@
     }
     
     void dumpLocal(PrintWriter pw, String prefix) {
-        pw.println(prefix + "receiver=IBinder "
-                + Integer.toHexString(System.identityHashCode(receiver.asBinder())));
-        pw.println(prefix + "app=" + app + " pid=" + pid + " uid=" + uid);
-        pw.println(prefix + "curBroadcast=" + curBroadcast
-                + " linkedToDeath=" + linkedToDeath);
+        pw.print(prefix); pw.print("app="); pw.print(app);
+            pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.println(uid);
+        if (curBroadcast != null || linkedToDeath) {
+            pw.print(prefix); pw.print("curBroadcast="); pw.print(curBroadcast);
+                pw.print(" linkedToDeath="); pw.println(linkedToDeath);
+        }
     }
     
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
+        Printer pr = new PrintWriterPrinter(pw);
         dumpLocal(pw, prefix);
         String p2 = prefix + "  ";
         final int N = size();
         for (int i=0; i<N; i++) {
             BroadcastFilter bf = get(i);
-            pw.println(prefix + "Filter #" + i + ": " + bf);
-            bf.dump(pw, p2);
+            pw.print(prefix); pw.print("Filter #"); pw.print(i);
+                    pw.print(": BroadcastFilter{");
+                    pw.print(Integer.toHexString(System.identityHashCode(bf)));
+                    pw.println('}');
+            bf.dumpInReceiverList(pw, pr, p2);
         }
     }
     
     public String toString() {
-        return "ReceiverList{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + pid + " " + (app != null ? app.processName : "(unknown name)")
-            + "/" + uid + " client "
-            + Integer.toHexString(System.identityHashCode(receiver.asBinder()))
-            + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ReceiverList{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(pid);
+        sb.append(' ');
+        sb.append((app != null ? app.processName : "(unknown name)"));
+        sb.append('/');
+        sb.append(uid);
+        sb.append((receiver.asBinder() instanceof Binder) ? " local:" : " remote:");
+        sb.append(Integer.toHexString(System.identityHashCode(receiver.asBinder())));
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 4b90600..fc93b69 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -77,41 +77,49 @@
     long restartTime;       // time of last restart.
     long nextRestartTime;   // time when restartDelay will expire.
 
+    String stringName;      // caching of toString
+    
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "intent=" + intent.getIntent());
-        pw.println(prefix + "packageName=" + packageName);
-        pw.println(prefix + "processName=" + processName);
-        pw.println(prefix + "permission=" + permission);
-        pw.println(prefix + "baseDir=" + baseDir+ " resDir=" + resDir + " dataDir=" + dataDir);
-        pw.println(prefix + "app=" + app);
-        pw.println(prefix + "isForeground=" + isForeground
-                + " lastActivity=" + lastActivity);
-        pw.println(prefix + "startRequested=" + startRequested
-              + " startId=" + lastStartId
-              + " executeNesting=" + executeNesting
-              + " executingStart=" + executingStart
-              + " crashCount=" + crashCount);
-        pw.println(prefix + "totalRestartCount=" + totalRestartCount
-                + " restartCount=" + restartCount
-                + " restartDelay=" + restartDelay
-                + " restartTime=" + restartTime
-                + " nextRestartTime=" + nextRestartTime);
+        pw.print(prefix); pw.print("intent={");
+                pw.print(intent.getIntent().toShortString(true, false));
+                pw.println('}');
+        pw.print(prefix); pw.print("packageName="); pw.println(packageName);
+        pw.print(prefix); pw.print("processName="); pw.println(processName);
+        if (permission != null) {
+            pw.print(prefix); pw.print("permission="); pw.println(permission);
+        }
+        pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
+                if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
+                pw.print(" dataDir="); pw.println(dataDir);
+        pw.print(prefix); pw.print("app="); pw.println(app);
+        pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
+                pw.print(" lastActivity="); pw.println(lastActivity);
+        pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
+                pw.print(" startId="); pw.print(lastStartId);
+                pw.print(" executeNesting="); pw.print(executeNesting);
+                pw.print(" executingStart="); pw.print(executingStart);
+                pw.print(" crashCount="); pw.println(crashCount);
+        pw.print(prefix); pw.print("totalRestartCount="); pw.print(totalRestartCount);
+                pw.print(" restartCount="); pw.print(restartCount);
+                pw.print(" restartDelay="); pw.print(restartDelay);
+                pw.print(" restartTime="); pw.print(restartTime);
+                pw.print(" nextRestartTime="); pw.println(nextRestartTime);
         if (bindings.size() > 0) {
-            pw.println(prefix + "Bindings:");
             Iterator<IntentBindRecord> it = bindings.values().iterator();
             while (it.hasNext()) {
                 IntentBindRecord b = it.next();
-                pw.println(prefix + "Binding " + b);
-                b.dump(pw, prefix + "  ");
+                pw.print(prefix); pw.print("* IntentBindRecord{");
+                        pw.print(Integer.toHexString(System.identityHashCode(b)));
+                        pw.println("}:");
+                b.dumpInService(pw, prefix + "  ");
             }
         }
         if (connections.size() > 0) {
-            pw.println(prefix + "All Connections:");
+            pw.print(prefix); pw.println("All Connections:");
             Iterator<ConnectionRecord> it = connections.values().iterator();
             while (it.hasNext()) {
                 ConnectionRecord c = it.next();
-                pw.println(prefix + "  " + c);
+                pw.print(prefix); pw.print("  "); pw.println(c);
             }
         }
     }
@@ -159,8 +167,13 @@
     }
     
     public String toString() {
-        return "ServiceRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + shortName + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("ServiceRecord{")
+            .append(Integer.toHexString(System.identityHashCode(this)))
+            .append(' ').append(shortName).append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index aab3736..bcb8f54 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -36,6 +36,8 @@
     boolean rootWasReset;   // True if the intent at the root of the task had
                             // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
 
+    String stringName;      // caching of toString() result.
+    
     TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
             boolean _clearOnBackground) {
         taskId = _taskId;
@@ -53,6 +55,8 @@
     }
     
     void setIntent(Intent _intent, ActivityInfo info) {
+        stringName = null;
+        
         if (info.targetActivity == null) {
             intent = _intent;
             realActivity = _intent != null ? _intent.getComponent() : null;
@@ -82,23 +86,63 @@
     }
     
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "clearOnBackground=" + clearOnBackground
-              + " numActivities=" + numActivities
-              + " rootWasReset=" + rootWasReset);
-        pw.println(prefix + "affinity=" + affinity);
-        pw.println(prefix + "intent=" + intent);
-        pw.println(prefix + "affinityIntent=" + affinityIntent);
-        pw.println(prefix + "origActivity=" + origActivity);
-        pw.println(prefix + "lastActiveTime=" + lastActiveTime
-                +" (inactive for " + (getInactiveDuration()/1000) + "s)");
+        if (clearOnBackground || numActivities != 0 || rootWasReset) {
+            pw.print(prefix); pw.print("clearOnBackground="); pw.print(clearOnBackground);
+                    pw.print(" numActivities="); pw.print(numActivities);
+                    pw.print(" rootWasReset="); pw.println(rootWasReset);
+        }
+        if (affinity != null) {
+            pw.print(prefix); pw.print("affinity="); pw.println(affinity);
+        }
+        if (intent != null) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append(prefix); sb.append("intent={");
+            intent.toShortString(sb, true, false);
+            sb.append('}');
+            pw.println(sb.toString());
+        }
+        if (affinityIntent != null) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append(prefix); sb.append("affinityIntent={");
+            affinityIntent.toShortString(sb, true, false);
+            sb.append('}');
+            pw.println(sb.toString());
+        }
+        if (origActivity != null) {
+            pw.print(prefix); pw.print("origActivity=");
+            pw.println(origActivity.flattenToShortString());
+        }
+        if (realActivity != null) {
+            pw.print(prefix); pw.print("realActivity=");
+            pw.println(realActivity.flattenToShortString());
+        }
+        pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+                pw.print(" (inactive for ");
+                pw.print((getInactiveDuration()/1000)); pw.println("s)");
     }
 
     public String toString() {
-        return "Task{" + taskId + " "
-                + (affinity != null ? affinity
-                        : (intent != null ? intent.getComponent().flattenToShortString()
-                                : affinityIntent != null ? affinityIntent.getComponent().flattenToShortString() : "??"))
-                + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("TaskRecord{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" #");
+        sb.append(taskId);
+        if (affinity != null) {
+            sb.append(" A ");
+            sb.append(affinity);
+        } else if (intent != null) {
+            sb.append(" I ");
+            sb.append(intent.getComponent().flattenToShortString());
+        } else if (affinityIntent != null) {
+            sb.append(" aI ");
+            sb.append(affinityIntent.getComponent().flattenToShortString());
+        } else {
+            sb.append(" ??");
+        }
+        sb.append('}');
+        return stringName = sb.toString();
     }
 }
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index fb7a745..ffa8a2a 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -30,6 +30,8 @@
     final HashSet<HistoryRecord> readActivities = new HashSet<HistoryRecord>();
     final HashSet<HistoryRecord> writeActivities = new HashSet<HistoryRecord>();
     
+    String stringName;
+    
     UriPermission(int _uid, Uri _uri) {
         uid = _uid;
         uri = _uri;
@@ -65,18 +67,29 @@
     }
     
     public String toString() {
-        return "UriPermission{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + uri + "}";
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("UriPermission{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        sb.append(uri);
+        sb.append('}');
+        return stringName = sb.toString();
     }
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + "  modeFlags=0x" + Integer.toHexString(modeFlags)
-                + " uid=" + uid 
-                + " globalModeFlags=0x"
-                + Integer.toHexString(globalModeFlags));
-        pw.println(prefix + "  readActivities=" + readActivities);
-        pw.println(prefix + "  writeActivities=" + writeActivities);
+        pw.print(prefix); pw.print("modeFlags=0x");
+                pw.print(Integer.toHexString(modeFlags));
+                pw.print(" uid="); pw.print(uid); 
+                pw.print(" globalModeFlags=0x");
+                pw.println(Integer.toHexString(globalModeFlags));
+        if (readActivities.size() != 0) {
+            pw.print(prefix); pw.print("readActivities="); pw.println(readActivities);
+        }
+        if (writeActivities.size() != 0) {
+            pw.print(prefix); pw.print("writeActivities="); pw.println(writeActivities);
+        }
     }
 }
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 3922f39..866334b 100755
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import com.android.internal.app.IUsageStats;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
@@ -37,11 +38,11 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 
 /**
  * This service collects the statistics associated with usage
@@ -53,6 +54,23 @@
     public static final String SERVICE_NAME = "usagestats";
     private static final boolean localLOGV = false;
     private static final String TAG = "UsageStats";
+    
+    // Current on-disk Parcel version
+    private static final int VERSION = 1004;
+
+    private static final int CHECKIN_VERSION = 3;
+    
+    private static final String FILE_PREFIX = "usage-";
+    
+    private static final int FILE_WRITE_INTERVAL = 30*60*1000; //ms
+    
+    private static final int MAX_NUM_FILES = 5;
+    
+    private static final int NUM_LAUNCH_TIME_BINS = 10;
+    private static final int[] LAUNCH_TIME_BINS = {
+        250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000
+    };
+    
     static IUsageStats sService;
     private Context mContext;
     // structure used to maintain statistics since the last checkin.
@@ -66,16 +84,48 @@
     // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
     private String mResumedPkg;
     private File mFile;
+    private String mFileLeaf;
     //private File mBackupFile;
-    private long mLastWriteRealTime;
-    private int _FILE_WRITE_INTERVAL = 30*60*1000; //ms
-    private static final String _PREFIX_DELIMIT=".";
-    private String mFilePrefix;
+    private long mLastWriteElapsedTime;
+    private File mDir;
     private Calendar mCal;
-    private static final int  _MAX_NUM_FILES = 10;
-    private long mLastTime;
+    private int mLastWriteDay;
+    
+    static class TimeStats {
+        int[] times = new int[NUM_LAUNCH_TIME_BINS];
+        
+        TimeStats() {
+        }
+        
+        void add(int val) {
+            final int[] bins = LAUNCH_TIME_BINS;
+            for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
+                if (val < bins[i]) {
+                    times[i]++;
+                    return;
+                }
+            }
+            times[NUM_LAUNCH_TIME_BINS-1]++;
+        }
+        
+        TimeStats(Parcel in) {
+            final int[] localTimes = times;
+            for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
+                localTimes[i] = in.readInt();
+            }
+        }
+        
+        void writeToParcel(Parcel out) {
+            final int[] localTimes = times;
+            for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
+                out.writeInt(localTimes[i]);
+            }
+        }
+    }
     
     private class PkgUsageStatsExtended {
+        final HashMap<String, TimeStats> mLaunchTimes
+                = new HashMap<String, TimeStats>();
         int mLaunchCount;
         long mUsageTime;
         long mPausedTime;
@@ -85,44 +135,106 @@
             mLaunchCount = 0;
             mUsageTime = 0;
         }
+        
+        PkgUsageStatsExtended(Parcel in) {
+            mLaunchCount = in.readInt();
+            mUsageTime = in.readLong();
+            if (localLOGV) Log.v(TAG, "Launch count: " + mLaunchCount
+                    + ", Usage time:" + mUsageTime);
+            
+            final int N = in.readInt();
+            if (localLOGV) Log.v(TAG, "Reading comps: " + N);
+            for (int i=0; i<N; i++) {
+                String comp = in.readString();
+                if (localLOGV) Log.v(TAG, "Component: " + comp);
+                TimeStats times = new TimeStats(in);
+                mLaunchTimes.put(comp, times);
+            }
+        }
+        
         void updateResume() {
             mLaunchCount ++;
             mResumedTime = SystemClock.elapsedRealtime();
         }
+        
         void updatePause() {
             mPausedTime =  SystemClock.elapsedRealtime();
             mUsageTime += (mPausedTime - mResumedTime);
         }
+        
+        void addLaunchTime(String comp, int millis) {
+            TimeStats times = mLaunchTimes.get(comp);
+            if (times == null) {
+                times = new TimeStats();
+                mLaunchTimes.put(comp, times);
+            }
+            times.add(millis);
+        }
+        
+        void writeToParcel(Parcel out) {
+            out.writeInt(mLaunchCount);
+            out.writeLong(mUsageTime);
+            final int N = mLaunchTimes.size();
+            out.writeInt(N);
+            if (N > 0) {
+                for (Map.Entry<String, TimeStats> ent : mLaunchTimes.entrySet()) {
+                    out.writeString(ent.getKey());
+                    TimeStats times = ent.getValue();
+                    times.writeToParcel(out);
+                }
+            }
+        }
+        
         void clear() {
+            mLaunchTimes.clear();
             mLaunchCount = 0;
             mUsageTime = 0;
         }
     }
     
-    UsageStatsService(String fileName) {
+    UsageStatsService(String dir) {
         mStats = new HashMap<String, PkgUsageStatsExtended>();
         mStatsLock = new Object();
         mFileLock = new Object();
-        mFilePrefix = fileName;
-        mCal = Calendar.getInstance();
+        mDir = new File(dir);
+        mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+        
+        mDir.mkdir();
+        
+        // Remove any old usage files from previous versions.
+        File parentDir = mDir.getParentFile();
+        String fList[] = parentDir.list();
+        if (fList != null) {
+            String prefix = mDir.getName() + ".";
+            int i = fList.length;
+            while (i > 0) {
+                i--;
+                if (fList[i].startsWith(prefix)) {
+                    Log.i(TAG, "Deleting old usage file: " + fList[i]);
+                    (new File(parentDir, fList[i])).delete();
+                }
+            }
+        }
+        
         // Update current stats which are binned by date
-        String uFileName = getCurrentDateStr(mFilePrefix);
-        mFile = new File(uFileName);
+        mFileLeaf = getCurrentDateStr(FILE_PREFIX);
+        mFile = new File(mDir, mFileLeaf);
         readStatsFromFile();
-        mLastWriteRealTime = SystemClock.elapsedRealtime();
-        mLastTime = new Date().getTime();
+        mLastWriteElapsedTime = SystemClock.elapsedRealtime();
+        // mCal was set by getCurrentDateStr(), want to use that same time.
+        mLastWriteDay = mCal.get(Calendar.DAY_OF_YEAR);
     }
 
     /*
      * Utility method to convert date into string.
      */
     private String getCurrentDateStr(String prefix) {
-        mCal.setTime(new Date());
+        mCal.setTimeInMillis(System.currentTimeMillis());
         StringBuilder sb = new StringBuilder();
         if (prefix != null) {
             sb.append(prefix);
-            sb.append(".");
         }
+        sb.append(mCal.get(Calendar.YEAR));
         int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
         if (mm < 10) {
             sb.append("0");
@@ -133,7 +245,6 @@
             sb.append("0");
         }
         sb.append(dd);
-        sb.append(mCal.get(Calendar.YEAR));
         return sb.toString();
     }
     
@@ -166,11 +277,20 @@
     
     private void readStatsFLOCK(File file) throws IOException {
         Parcel in = getParcelForFile(file);
-        while (in.dataAvail() > 0) {
+        int vers = in.readInt();
+        if (vers != VERSION) {
+            Log.w(TAG, "Usage stats version changed; dropping");
+            return;
+        }
+        int N = in.readInt();
+        while (N > 0) {
+            N--;
             String pkgName = in.readString();
-            PkgUsageStatsExtended pus = new PkgUsageStatsExtended();
-            pus.mLaunchCount = in.readInt();
-            pus.mUsageTime = in.readLong();
+            if (pkgName == null) {
+                break;
+            }
+            if (localLOGV) Log.v(TAG, "Reading package #" + N + ": " + pkgName);
+            PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
             synchronized (mStatsLock) {
                 mStats.put(pkgName, pus);
             }
@@ -178,27 +298,18 @@
     }
 
     private ArrayList<String> getUsageStatsFileListFLOCK() {
-        File dir = getUsageFilesDir();
-        if (dir == null) {
-            Log.w(TAG, "Couldnt find writable directory for usage stats file");
-            return null;
-        }
         // Check if there are too many files in the system and delete older files
-        String fList[] = dir.list();
+        String fList[] = mDir.list();
         if (fList == null) {
             return null;
         }
-        File pre = new File(mFilePrefix);
-        String filePrefix = pre.getName();
-        // file name followed by dot
-        int prefixLen = filePrefix.length()+1;
         ArrayList<String> fileList = new ArrayList<String>();
         for (String file : fList) {
-            int index = file.indexOf(filePrefix);
-            if (index == -1) {
+            if (!file.startsWith(FILE_PREFIX)) {
                 continue;
             }
             if (file.endsWith(".bak")) {
+                (new File(mDir, file)).delete();
                 continue;
             }
             fileList.add(file);
@@ -206,20 +317,7 @@
         return fileList;
     }
     
-    private File getUsageFilesDir() {
-        if (mFilePrefix == null) {
-            return null;
-        }
-        File pre = new File(mFilePrefix);
-        return new File(pre.getParent());
-    }
-    
     private void checkFileLimitFLOCK() {
-        File dir = getUsageFilesDir();
-        if (dir == null) {
-            Log.w(TAG, "Couldnt find writable directory for usage stats file");
-            return;
-        }
         // Get all usage stats output files
         ArrayList<String> fileList = getUsageStatsFileListFLOCK();
         if (fileList == null) {
@@ -227,49 +325,54 @@
             return;
         }
         int count = fileList.size();
-        if (count <= _MAX_NUM_FILES) {
+        if (count <= MAX_NUM_FILES) {
             return;
         }
         // Sort files
         Collections.sort(fileList);
-        count -= _MAX_NUM_FILES;
+        count -= MAX_NUM_FILES;
         // Delete older files
         for (int i = 0; i < count; i++) {
             String fileName = fileList.get(i);
-            File file = new File(dir, fileName);
-            Log.i(TAG, "Deleting file : "+fileName);
+            File file = new File(mDir, fileName);
+            Log.i(TAG, "Deleting usage file : " + fileName);
             file.delete();
         }
     }
     
-    private void writeStatsToFile() {
+    private void writeStatsToFile(boolean force) {
         synchronized (mFileLock) {
-            long currTime = new Date().getTime();
-            boolean dayChanged =  ((currTime - mLastTime) >= (24*60*60*1000));
-            long currRealTime = SystemClock.elapsedRealtime();
-            if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) &&
-                    (!dayChanged)) {
-                // wait till the next update
-                return;
+            mCal.setTimeInMillis(System.currentTimeMillis());
+            final int curDay = mCal.get(Calendar.DAY_OF_YEAR);
+            // Determine if the day changed...  note that this will be wrong
+            // if the year has changed but we are in the same day of year...
+            // we can probably live with this.
+            final boolean dayChanged =  curDay != mLastWriteDay;
+            long currElapsedTime = SystemClock.elapsedRealtime();
+            if (!force) {
+                if (((currElapsedTime-mLastWriteElapsedTime) < FILE_WRITE_INTERVAL) &&
+                        (!dayChanged)) {
+                    // wait till the next update
+                    return;
+                }
             }
             // Get the most recent file
-            String todayStr = getCurrentDateStr(mFilePrefix);
+            mFileLeaf = getCurrentDateStr(FILE_PREFIX);
             // Copy current file to back up
             File backupFile =  new File(mFile.getPath() + ".bak");
             mFile.renameTo(backupFile);
             try {
-                checkFileLimitFLOCK();
-                mFile.createNewFile();
                 // Write mStats to file
                 writeStatsFLOCK();
-                mLastWriteRealTime = currRealTime;
-                mLastTime = currTime;
+                mLastWriteElapsedTime = currElapsedTime;
                 if (dayChanged) {
+                    mLastWriteDay = curDay;
                     // clear stats
                     synchronized (mStats) {
                         mStats.clear();
                     }
-                    mFile = new File(todayStr);
+                    mFile = new File(mDir, mFileLeaf);
+                    checkFileLimitFLOCK();
                 }
                 // Delete the backup file
                 if (backupFile != null) {
@@ -278,6 +381,7 @@
             } catch (IOException e) {
                 Log.w(TAG, "Failed writing stats to file:" + mFile);
                 if (backupFile != null) {
+                    mFile.delete();
                     backupFile.renameTo(mFile);
                 }
             }
@@ -286,22 +390,26 @@
 
     private void writeStatsFLOCK() throws IOException {
         FileOutputStream stream = new FileOutputStream(mFile);
-        Parcel out = Parcel.obtain();
-        writeStatsToParcelFLOCK(out);
-        stream.write(out.marshall());
-        out.recycle();
-        stream.flush();
-        stream.close();
+        try {
+            Parcel out = Parcel.obtain();
+            writeStatsToParcelFLOCK(out);
+            stream.write(out.marshall());
+            out.recycle();
+            stream.flush();
+        } finally {
+            stream.close();
+        }
     }
 
     private void writeStatsToParcelFLOCK(Parcel out) {
         synchronized (mStatsLock) {
+            out.writeInt(VERSION);
             Set<String> keys = mStats.keySet();
+            out.writeInt(keys.size());
             for (String key : keys) {
                 PkgUsageStatsExtended pus = mStats.get(key);
                 out.writeString(key);
-                out.writeInt(pus.mLaunchCount);
-                out.writeLong(pus.mUsageTime);
+                pus.writeToParcel(out);
             }
         }
     }
@@ -311,6 +419,11 @@
         ServiceManager.addService(SERVICE_NAME, asBinder());
     }
     
+    public void shutdown() {
+        Log.w(TAG, "Writing usage stats before shutdown...");
+        writeStatsToFile(true);
+    }
+    
     public static IUsageStats getService() {
         if (sService != null) {
             return sService;
@@ -355,6 +468,10 @@
             return;
         }
         if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
+        
+        // Persist current data to file if needed.
+        writeStatsToFile(false);
+        
         synchronized (mStatsLock) {
             PkgUsageStatsExtended pus = mStats.get(pkgName);
             if (pus == null) {
@@ -364,8 +481,25 @@
             }
             pus.updatePause();
         }
-        // Persist data to file
-        writeStatsToFile();
+    }
+    
+    public void noteLaunchTime(ComponentName componentName, int millis) {
+        enforceCallingPermission();
+        String pkgName;
+        if ((componentName == null) ||
+                ((pkgName = componentName.getPackageName()) == null)) {
+            return;
+        }
+        
+        // Persist current data to file if needed.
+        writeStatsToFile(false);
+        
+        synchronized (mStatsLock) {
+            PkgUsageStatsExtended pus = mStats.get(pkgName);
+            if (pus != null) {
+                pus.addLaunchTime(componentName.getClassName(), millis);
+            }
+        }
     }
     
     public void enforceCallingPermission() {
@@ -432,27 +566,25 @@
         }
     }
     
-    private void collectDumpInfoFLOCK(PrintWriter pw, String[] args) {
+    private void collectDumpInfoFLOCK(PrintWriter pw, boolean isCompactOutput,
+            boolean deleteAfterPrint) {
         List<String> fileList = getUsageStatsFileListFLOCK();
         if (fileList == null) {
             return;
         }
-        final boolean isCheckinRequest = scanArgs(args, "-c");
         Collections.sort(fileList);
-        File usageFile = new File(mFilePrefix);
-        String dirName = usageFile.getParent();
-        File dir = new File(dirName);
-        String filePrefix = usageFile.getName();
-        // file name followed by dot
-        int prefixLen = filePrefix.length()+1;
-        String todayStr = getCurrentDateStr(null);
         for (String file : fileList) {
-            File dFile = new File(dir, file);
-            String dateStr = file.substring(prefixLen);
+            if (deleteAfterPrint && file.equalsIgnoreCase(mFileLeaf)) {
+                // In this mode we don't print the current day's stats, since
+                // they are incomplete.
+                continue;
+            }
+            File dFile = new File(mDir, file);
+            String dateStr = file.substring(FILE_PREFIX.length());
             try {
                 Parcel in = getParcelForFile(dFile);
-                collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest);
-                if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) {
+                collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput);
+                if (deleteAfterPrint) {
                     // Delete old file after collecting info only for checkin requests
                     dFile.delete();
                 }
@@ -466,40 +598,101 @@
     }
     
     private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw,
-            String date, boolean isCheckinRequest) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Date:");
-        sb.append(date);
-        boolean first = true;
-        while (in.dataAvail() > 0) {
-            String pkgName = in.readString();
-            int launchCount = in.readInt();
-            long usageTime = in.readLong();
-            if (isCheckinRequest) {
-                if (!first) {
-                    sb.append(",");
-                }
-                sb.append(pkgName);
-                sb.append(",");
-                sb.append(launchCount);
-                sb.append(",");
-                sb.append(usageTime);
-                sb.append("ms");
-            } else {
-                if (first) {
-                    sb.append("\n");
-                }
-                sb.append("pkg=");
-                sb.append(pkgName);
-                sb.append(", launchCount=");
-                sb.append(launchCount);
-                sb.append(", usageTime=");
-                sb.append(usageTime);
-                sb.append(" ms\n");
-            }
-            first = false;
+            String date, boolean isCompactOutput) {
+        StringBuilder sb = new StringBuilder(512);
+        if (isCompactOutput) {
+            sb.append("D:");
+            sb.append(CHECKIN_VERSION);
+            sb.append(',');
+        } else {
+            sb.append("Date: ");
         }
-        pw.write(sb.toString());
+        
+        sb.append(date);
+        
+        int vers = in.readInt();
+        if (vers != VERSION) {
+            sb.append(" (old data version)");
+            pw.println(sb.toString());
+            return;
+        }
+        
+        pw.println(sb.toString());
+        int N = in.readInt();
+        
+        while (N > 0) {
+            N--;
+            String pkgName = in.readString();
+            if (pkgName == null) {
+                break;
+            }
+            sb.setLength(0);
+            PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
+            if (isCompactOutput) {
+                sb.append("P:");
+                sb.append(pkgName);
+                sb.append(",");
+                sb.append(pus.mLaunchCount);
+                sb.append(",");
+                sb.append(pus.mUsageTime);
+                sb.append('\n');
+                final int NC = pus.mLaunchTimes.size();
+                if (NC > 0) {
+                    for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
+                        sb.append("A:");
+                        sb.append(ent.getKey());
+                        TimeStats times = ent.getValue();
+                        for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
+                            sb.append(",");
+                            sb.append(times.times[i]);
+                        }
+                        sb.append('\n');
+                    }
+                }
+                
+            } else {
+                sb.append("  ");
+                sb.append(pkgName);
+                sb.append(": ");
+                sb.append(pus.mLaunchCount);
+                sb.append(" times, ");
+                sb.append(pus.mUsageTime);
+                sb.append(" ms");
+                sb.append('\n');
+                final int NC = pus.mLaunchTimes.size();
+                if (NC > 0) {
+                    for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
+                        sb.append("    ");
+                        sb.append(ent.getKey());
+                        TimeStats times = ent.getValue();
+                        int lastBin = 0;
+                        boolean first = true;
+                        for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
+                            if (times.times[i] != 0) {
+                                sb.append(first ? ": " : ", ");
+                                sb.append(lastBin);
+                                sb.append('-');
+                                sb.append(LAUNCH_TIME_BINS[i]);
+                                sb.append('=');
+                                sb.append(times.times[i]);
+                                first = false;
+                            }
+                            lastBin = LAUNCH_TIME_BINS[i];
+                        }
+                        if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
+                            sb.append(first ? ": " : ", ");
+                            sb.append(">=");
+                            sb.append(lastBin);
+                            sb.append('=');
+                            sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
+                        }
+                        sb.append('\n');
+                    }
+                }
+            }
+            
+            pw.write(sb.toString());
+        }
     }
     
     /**
@@ -524,8 +717,19 @@
      * The data persisted to file is parsed and the stats are computed. 
      */
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final boolean isCheckinRequest = scanArgs(args, "--checkin");
+        final boolean isCompactOutput = isCheckinRequest || scanArgs(args, "-c");
+        final boolean deleteAfterPrint = isCheckinRequest || scanArgs(args, "-d");
+        
+        // Make sure the current stats are written to the file.  This
+        // doesn't need to be done if we are deleting files after printing,
+        // since it that case we won't print the current stats.
+        if (!deleteAfterPrint) {
+            writeStatsToFile(true);
+        }
+        
         synchronized (mFileLock) {
-            collectDumpInfoFLOCK(pw, args);
+            collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint);
         }
     }
 
diff --git a/services/java/com/android/server/status/IconMerger.java b/services/java/com/android/server/status/IconMerger.java
index 37fdbfb..5b80638 100644
--- a/services/java/com/android/server/status/IconMerger.java
+++ b/services/java/com/android/server/status/IconMerger.java
@@ -8,8 +8,6 @@
 
 
 public class IconMerger extends LinearLayout {
-    private static final boolean SPEW = false;
-
     StatusBarService service;
     StatusBarIcon moreIcon;
 
@@ -29,7 +27,7 @@
         int fitRight = -1;
         for (i=N-1; i>=0; i--) {
             final View child = getChildAt(i);
-            if (child != null && child.getVisibility() != GONE) {
+            if (child.getVisibility() != GONE) {
                 fitRight = child.getRight();
                 break;
             }
@@ -45,7 +43,7 @@
                 moreView = child;
                 startIndex = i+1;
             }
-            else if (child != null && child.getVisibility() != GONE) {
+            else if (child.getVisibility() != GONE) {
                 fitLeft = child.getLeft();
                 break;
             }
@@ -71,7 +69,7 @@
         int number = 0;
         for (i=startIndex; i<N; i++) {
             final View child = getChildAt(i);
-            if (child != null && child.getVisibility() != GONE) {
+            if (child.getVisibility() != GONE) {
                 int childLeft = child.getLeft();
                 int childRight = child.getRight();
                 if (childLeft < breakingPoint) {
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 713cd13..0b161d6 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -16,13 +16,6 @@
 
 package com.android.server.status;
 
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsLocationProvider;
-import com.android.internal.telephony.SimCard;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.server.am.BatteryStatsService;
-
 import android.app.AlertDialog;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
@@ -59,6 +52,14 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.R;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.location.GpsLocationProvider;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.TtyIntent;
+import com.android.server.am.BatteryStatsService;
+
 import java.util.Calendar;
 import java.util.TimeZone;
 
@@ -110,7 +111,10 @@
     // phone
     private TelephonyManager mPhone;
     private IBinder mPhoneIcon;
+
+    //***** Signal strength icons
     private IconData mPhoneData;
+    //GSM/UMTS
     private static final int[] sSignalImages = new int[] {
             com.android.internal.R.drawable.stat_sys_signal_0,
             com.android.internal.R.drawable.stat_sys_signal_1,
@@ -125,7 +129,32 @@
             com.android.internal.R.drawable.stat_sys_r_signal_3,
             com.android.internal.R.drawable.stat_sys_r_signal_4
         };
+    //CDMA
+    private static final int[] sSignalImages_cdma = new int[] {
+        com.android.internal.R.drawable.stat_sys_signal_0_cdma,
+        com.android.internal.R.drawable.stat_sys_signal_1_cdma,
+        com.android.internal.R.drawable.stat_sys_signal_2_cdma,
+        com.android.internal.R.drawable.stat_sys_signal_3_cdma,
+        com.android.internal.R.drawable.stat_sys_signal_4_cdma
+    };
+    private static final int[] sSignalImages_r_cdma = new int[] {
+        com.android.internal.R.drawable.stat_sys_r_signal_0_cdma,
+        com.android.internal.R.drawable.stat_sys_r_signal_1_cdma,
+        com.android.internal.R.drawable.stat_sys_r_signal_2_cdma,
+        com.android.internal.R.drawable.stat_sys_r_signal_3_cdma,
+        com.android.internal.R.drawable.stat_sys_r_signal_4_cdma
+    };
+    private static final int[] sSignalImages_ra_cdma = new int[] {
+        com.android.internal.R.drawable.stat_sys_ra_signal_0_cdma,
+        com.android.internal.R.drawable.stat_sys_ra_signal_1_cdma,
+        com.android.internal.R.drawable.stat_sys_ra_signal_2_cdma,
+        com.android.internal.R.drawable.stat_sys_ra_signal_3_cdma,
+        com.android.internal.R.drawable.stat_sys_ra_signal_4_cdma
+    };
+
+    //***** Data connection icons
     private int[] mDataIconList = sDataNetType_g;
+    //GSM/UMTS
     private static final int[] sDataNetType_g = new int[] {
             com.android.internal.R.drawable.stat_sys_data_connected_g,
             com.android.internal.R.drawable.stat_sys_data_in_g,
@@ -144,12 +173,25 @@
             com.android.internal.R.drawable.stat_sys_data_out_e,
             com.android.internal.R.drawable.stat_sys_data_inandout_e,
         };
+    //CDMA
+    private static final int[] sDataNetType_evdo = new int[] {
+        com.android.internal.R.drawable.stat_sys_data_connected_evdo,
+        com.android.internal.R.drawable.stat_sys_data_in_evdo,
+        com.android.internal.R.drawable.stat_sys_data_out_evdo,
+        com.android.internal.R.drawable.stat_sys_data_inandout_evdo,
+    };
+    private static final int[] sDataNetType_1xrtt = new int[] {
+        com.android.internal.R.drawable.stat_sys_data_connected_1xrtt,
+        com.android.internal.R.drawable.stat_sys_data_in_1xrtt,
+        com.android.internal.R.drawable.stat_sys_data_out_1xrtt,
+        com.android.internal.R.drawable.stat_sys_data_inandout_1xrtt,
+    };
+
     // Assume it's all good unless we hear otherwise.  We don't always seem
     // to get broadcasts that it *is* there.
-    SimCard.State mSimState = SimCard.State.READY;
+    IccCard.State mSimState = IccCard.State.READY;
     int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
     int mDataState = TelephonyManager.DATA_DISCONNECTED;
-    int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
     ServiceState mServiceState;
     int mSignalAsu = -1;
@@ -163,7 +205,7 @@
     private IBinder mVolumeIcon;
     private IconData mVolumeData;
     private boolean mVolumeVisible;
-    
+
     // bluetooth device status
     private IBinder mBluetoothIcon;
     private IconData mBluetoothData;
@@ -202,6 +244,11 @@
     private IBinder mSyncActiveIcon;
     private IBinder mSyncFailingIcon;
 
+    // TTY mode
+    // Icon lit when TTY mode is enabled
+    private IBinder mTTYModeIcon;
+    private IconData mTTYModeEnableIconData;
+
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -250,6 +297,9 @@
             else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
                 updateSimState(intent);
             }
+            else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
+                updateTTY(intent);
+            }
         }
     };
 
@@ -294,7 +344,13 @@
         mWifiIcon = service.addIcon(mWifiData, null);
         service.setIconVisibility(mWifiIcon, false);
         // wifi will get updated by the sticky intents
-        
+
+        // TTY status
+        mTTYModeEnableIconData = IconData.makeIcon("tty",
+                null, com.android.internal.R.drawable.stat_sys_tty_mode, 0, 0);
+        mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
+        service.setIconVisibility(mTTYModeIcon, false);
+
         // bluetooth status
         mBluetoothData = IconData.makeIcon("bluetooth",
                 null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
@@ -362,6 +418,7 @@
         filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
         filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
     }
 
@@ -506,7 +563,7 @@
                 com.android.internal.R.styleable.Theme);
         lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
         a.recycle();
-        
+
         lp.setTitle("Battery");
 
         TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
@@ -642,26 +699,26 @@
     
 
     private final void updateSimState(Intent intent) {
-        String stateExtra = intent.getStringExtra(SimCard.INTENT_KEY_SIM_STATE);
-        if (SimCard.INTENT_VALUE_SIM_ABSENT.equals(stateExtra)) {
-            mSimState = SimCard.State.ABSENT;
+        String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+        if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+            mSimState = IccCard.State.ABSENT;
         }
-        else if (SimCard.INTENT_VALUE_SIM_READY.equals(stateExtra)) {
-            mSimState = SimCard.State.READY;
+        else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+            mSimState = IccCard.State.READY;
         }
-        else if (SimCard.INTENT_VALUE_SIM_LOCKED.equals(stateExtra)) {
-            final String lockedReason = intent.getStringExtra(SimCard.INTENT_KEY_LOCKED_REASON);
-            if (SimCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
-                mSimState = SimCard.State.PIN_REQUIRED;
+        else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+            final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+            if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = IccCard.State.PIN_REQUIRED;
             } 
-            else if (SimCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
-                mSimState = SimCard.State.PUK_REQUIRED;
+            else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = IccCard.State.PUK_REQUIRED;
             }
             else {
-                mSimState = SimCard.State.NETWORK_LOCKED;
+                mSimState = IccCard.State.NETWORK_LOCKED;
             }
         } else {
-            mSimState = SimCard.State.UNKNOWN;
+            mSimState = IccCard.State.UNKNOWN;
         }
         updateDataIcon();
     }
@@ -707,28 +764,54 @@
         else asu = 1;
 
         int[] iconList;
-        if (mPhone.isNetworkRoaming()) {
+        if (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+            switch(ss.getExtendedCdmaRoaming()) {
+            case ServiceState.REGISTRATION_STATE_ROAMING:
+                iconList = this.sSignalImages_r_cdma;
+                break;
+            case ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE:
+                iconList = this.sSignalImages_ra_cdma;
+                break;
+            default:
+                iconList = this.sSignalImages_cdma;
+            break;
+            }
+        } else if (mPhone.isNetworkRoaming()) {
             iconList = sSignalImages_r;
         } else {
             iconList = sSignalImages;
         }
-        
+
         mPhoneData.iconId = iconList[asu];
         mService.updateIcon(mPhoneIcon, mPhoneData, null);
     }
 
     private final void updateDataNetType() {
-        mDataNetType = mPhone.getNetworkType();
-        switch (mDataNetType) {
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-                mDataIconList = sDataNetType_e;
-                break;
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-                mDataIconList = sDataNetType_3g;
-                break;
-            default:
-                mDataIconList = sDataNetType_g;
-                break;
+        int net = mPhone.getNetworkType();
+        ServiceState ss = this.mServiceState;
+
+        switch (net) {
+
+        case TelephonyManager.NETWORK_TYPE_EDGE:
+            mDataIconList = sDataNetType_e;
+            break;
+        case TelephonyManager.NETWORK_TYPE_UMTS:
+            mDataIconList = sDataNetType_3g;
+            break;
+        case TelephonyManager.NETWORK_TYPE_CDMA:
+            // display 1xRTT for IS95A/B
+            mDataIconList = this.sDataNetType_1xrtt;
+            break;
+        case TelephonyManager.NETWORK_TYPE_1xRTT:
+            mDataIconList = this.sDataNetType_1xrtt;
+            break;
+        case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
+        case TelephonyManager.NETWORK_TYPE_EVDO_A:
+            mDataIconList = sDataNetType_evdo;
+            break;
+        default:
+            mDataIconList = sDataNetType_g;
+        break;
         }
     }
 
@@ -736,9 +819,9 @@
         int iconId;
         boolean visible = true;
 
-        if (mSimState == SimCard.State.READY || mSimState == SimCard.State.UNKNOWN) {
+        if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
             int data = mDataState;
-            
+
             int[] list = mDataIconList;
 
             ServiceState ss = mServiceState;
@@ -775,7 +858,7 @@
         }
         long ident = Binder.clearCallingIdentity();
         try {
-            mBatteryStats.notePhoneDataConnectionState(mDataNetType, visible);
+            mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -865,10 +948,10 @@
                 } else {
                     iconId = sWifiSignalImages[mLastWifiSignalLevel];
                 }
-                
+
                 // Show the icon since wi-fi is connected
                 mService.setIconVisibility(mWifiIcon, true);
-                
+
             } else {
                 mLastWifiSignalLevel = -1;
                 mIsWifiConnected = false;
@@ -914,6 +997,24 @@
         }
     }
 
+    private final void updateTTY(Intent intent) {       
+        final String action = intent.getAction();
+        final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
+
+        Log.i(TAG, "updateTTY: enabled: " + enabled);
+
+        if (enabled) {
+            // TTY is on
+            Log.i(TAG, "updateTTY: set TTY on");
+            mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
+            mService.setIconVisibility(mTTYModeIcon, true);          
+        } else {
+            // TTY is off
+            Log.i(TAG, "updateTTY: set TTY off");
+            mService.setIconVisibility(mTTYModeIcon, false);           
+        }
+    }
+
     private class StatusBarHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -927,3 +1028,6 @@
         }
     }
 }
+
+
+