donut snapshot
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
new file mode 100644
index 0000000..c205fc0
--- /dev/null
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -0,0 +1,668 @@
+/*
+ ** Copyright 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 static android.util.Config.LOGV;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.HandlerCaller.SomeArgs;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.IEventListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+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.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class is instantiated by the system as a system level service and can be
+ * accessed only by the system. The task of this service is to be a centralized
+ * event dispatch for {@link AccessibilityEvent}s generated across all processes
+ * on the device. Events are dispatched to {@link AccessibilityService}s.
+ *
+ * @hide
+ */
+public class AccessibilityManagerService extends IAccessibilityManager.Stub
+        implements HandlerCaller.Callback {
+
+    private static final String LOG_TAG = "AccessibilityManagerService";
+
+    private static int sIdCounter = 0;
+
+    private static final int OWN_PROCESS_ID = android.os.Process.myPid();
+
+    private static final int DO_SET_SERVICE_INFO = 10;
+
+    final HandlerCaller mCaller;
+
+    final Context mContext;
+
+    final Object mLock = new Object();
+
+    final List<Service> mServices = new ArrayList<Service>();
+
+    final List<IAccessibilityManagerClient> mClients =
+        new ArrayList<IAccessibilityManagerClient>();
+
+    final Map<ComponentName, Service> mComponentNameToServiceMap =
+        new HashMap<ComponentName, Service>();
+
+    private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
+
+    private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+
+    private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
+
+    private PackageManager mPackageManager;
+
+    private int mHandledFeedbackTypes = 0;
+
+    private boolean mIsEnabled;
+
+    /**
+     * Handler for delayed event dispatch.
+     */
+    private Handler mHandler = new Handler() {
+
+        @Override
+        public void handleMessage(Message message) {
+            Service service = (Service) message.obj;
+            int eventType = message.arg1;
+
+            synchronized (mLock) {
+                notifyEventListenerLocked(service, eventType);
+                AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
+                service.mPendingEvents.remove(eventType);
+                tryRecycleLocked(oldEvent);
+            }
+        }
+    };
+
+    /**
+     * Creates a new instance.
+     *
+     * @param context A {@link Context} instance.
+     */
+    AccessibilityManagerService(Context context) {
+        mContext = context;
+        mPackageManager = mContext.getPackageManager();
+        mCaller = new HandlerCaller(context, this);
+
+        registerPackageChangeAndBootCompletedBroadcastReceiver();
+        registerSettingsContentObservers();
+
+        synchronized (mLock) {
+            populateAccessibilityServiceListLocked();
+        }
+    }
+
+    /**
+     * Registers a {@link BroadcastReceiver} for the events of
+     * adding/changing/removing/restarting a package and boot completion.
+     */
+    private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
+        Context context = mContext;
+
+        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                synchronized (mLock) {
+                    populateAccessibilityServiceListLocked();
+                    manageServicesLocked();
+
+                    if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
+                        mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+                                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+                        updateClientsLocked();
+                    }
+                }
+            }
+        };
+
+        // package changes
+        IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        packageFilter.addDataScheme("package");
+        context.registerReceiver(broadcastReceiver, packageFilter);
+
+        // boot completed
+        IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+        mContext.registerReceiver(broadcastReceiver, bootFiler);
+    }
+
+    /**
+     * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
+     * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
+     */
+    private void registerSettingsContentObservers() {
+        ContentResolver contentResolver = mContext.getContentResolver();
+
+        Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
+        contentResolver.registerContentObserver(enabledUri, false,
+            new ContentObserver(new Handler()) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+
+                    mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+
+                    synchronized (mLock) {
+                        if (mIsEnabled) {
+                            manageServicesLocked();
+                        } else {
+                            unbindAllServicesLocked();
+                        }
+                        updateClientsLocked();
+                    }
+                }
+            });
+
+        Uri providersUri =
+            Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        contentResolver.registerContentObserver(providersUri, false,
+            new ContentObserver(new Handler()) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+
+                    synchronized (mLock) {
+                        manageServicesLocked();
+                    }
+                }
+            });
+    }
+
+    public void addClient(IAccessibilityManagerClient client) {
+        synchronized (mLock) {
+            try {
+                client.setEnabled(mIsEnabled);
+                mClients.add(client);
+            } catch (RemoteException re) {
+                Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
+            }
+        }
+    }
+
+    public boolean sendAccessibilityEvent(AccessibilityEvent event) {
+        synchronized (mLock) {
+            notifyAccessibilityServicesDelayedLocked(event, false);
+            notifyAccessibilityServicesDelayedLocked(event, true);
+        }
+        // event not scheduled for dispatch => recycle
+        if (mHandledFeedbackTypes == 0) {
+            event.recycle();
+        } else {
+            mHandledFeedbackTypes = 0;
+        }
+
+        return (OWN_PROCESS_ID != Binder.getCallingPid());
+    }
+
+    public List<ServiceInfo> getAccessibilityServiceList() {
+        synchronized (mLock) {
+            return mInstalledServices;
+        }
+    }
+
+    public void interrupt() {
+        synchronized (mLock) {
+            for (int i = 0, count = mServices.size(); i < count; i++) {
+                Service service = mServices.get(i);
+                try {
+                    service.mServiceInterface.onInterrupt();
+                } catch (RemoteException re) {
+                    if (re instanceof DeadObjectException) {
+                        Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
+                        if (removeDeadServiceLocked(service)) {
+                            count--;
+                            i--;
+                        }
+                    } else {
+                        Log.e(LOG_TAG, "Error during sending interrupt request to "
+                                + service.mService, re);
+                    }
+                }
+            }
+        }
+    }
+
+    public void executeMessage(Message message) {
+        switch (message.what) {
+            case DO_SET_SERVICE_INFO:
+                SomeArgs arguments = ((SomeArgs) message.obj);
+
+                AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
+                Service service = (Service) arguments.arg2;
+
+                synchronized (mLock) {
+                    service.mEventTypes = info.eventTypes;
+                    service.mFeedbackType = info.feedbackType;
+                    String[] packageNames = info.packageNames;
+                    if (packageNames != null) {
+                        service.mPackageNames.addAll(Arrays.asList(packageNames));
+                    }
+                    service.mNotificationTimeout = info.notificationTimeout;
+                    service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+                }
+                return;
+            default:
+                Log.w(LOG_TAG, "Unknown message type: " + message.what);
+        }
+    }
+
+    /**
+     * Populates the cached list of installed {@link AccessibilityService}s.
+     */
+    private void populateAccessibilityServiceListLocked() {
+        mInstalledServices.clear();
+
+        List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
+                new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
+
+        for (int i = 0, count = installedServices.size(); i < count; i++) {
+            mInstalledServices.add(installedServices.get(i).serviceInfo);
+        }
+    }
+
+    /**
+     * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
+     * and denotes the period after the last event before notifying the service.
+     *
+     * @param event The event.
+     * @param isDefault True to notify default listeners, not default services.
+     */
+    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
+            boolean isDefault) {
+        for (int i = 0, count = mServices.size(); i < count; i++) {
+            Service service = mServices.get(i);
+
+            if (service.mIsDefault == isDefault) {
+                if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
+                    mHandledFeedbackTypes |= service.mFeedbackType;
+                    notifyAccessibilityServiceDelayedLocked(service, event);
+                }
+            }
+        }
+    }
+
+    /**
+     * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
+     * and denotes the period after the last event before notifying the service.
+     *
+     * @param service The service.
+     * @param event The event.
+     */
+    private void notifyAccessibilityServiceDelayedLocked(Service service,
+            AccessibilityEvent event) {
+        synchronized (mLock) {
+            int eventType = event.getEventType();
+            AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
+            service.mPendingEvents.put(eventType, event);
+
+            int what = eventType | (service.mId << 16);
+            if (oldEvent != null) {
+                mHandler.removeMessages(what);
+                tryRecycleLocked(oldEvent);
+            }
+
+            Message message = mHandler.obtainMessage(what, service);
+            message.arg1 = event.getEventType();
+            mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
+        }
+    }
+
+    /**
+     * Recycles an event if it can be safely recycled. The condition is that no
+     * not notified service is interested in the event.
+     *
+     * @param event The event.
+     */
+    private void tryRecycleLocked(AccessibilityEvent event) {
+        int eventType = event.getEventType();
+        List<Service> services = mServices;
+
+        // linear in the number of service which is not large
+        for (int i = 0, count = services.size(); i < count; i++) {
+            Service service = services.get(i);
+            if (service.mPendingEvents.get(eventType) == event) {
+                return;
+            }
+        }
+
+        event.recycle();
+    }
+
+    /**
+     * Notifies a service for a scheduled event given the event type.
+     *
+     * @param service The service.
+     * @param eventType The type of the event to dispatch.
+     */
+    private void notifyEventListenerLocked(Service service, int eventType) {
+        IEventListener listener = service.mServiceInterface;
+        AccessibilityEvent event = service.mPendingEvents.get(eventType);
+
+        try {
+            listener.onAccessibilityEvent(event);
+            if (LOGV) {
+                Log.i(LOG_TAG, "Event " + event + " sent to " + listener);
+            }
+        } catch (RemoteException re) {
+            if (re instanceof DeadObjectException) {
+                Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
+                synchronized (mLock) {
+                    removeDeadServiceLocked(service);
+                }
+            } else {
+                Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
+            }
+        }
+    }
+
+    /**
+     * Removes a dead service.
+     *
+     * @param service The service.
+     * @return True if the service was removed, false otherwise.
+     */
+    private boolean removeDeadServiceLocked(Service service) {
+        mServices.remove(service);
+        mHandler.removeMessages(service.mId);
+
+        if (LOGV) {
+            Log.i(LOG_TAG, "Dead service " + service.mService + " removed");
+        }
+
+        if (mServices.isEmpty()) {
+            mIsEnabled = false;
+            updateClientsLocked();
+        }
+
+        return true;
+    }
+
+    /**
+     * Determines if given event can be dispatched to a service based on the package of the
+     * event source and already notified services for that event type. Specifically, a
+     * service is notified if it is interested in events from the package and no other service
+     * providing the same feedback type has been notified. Exception are services the
+     * provide generic feedback (feedback type left as a safety net for unforeseen feedback
+     * types) which are always notified.
+     *
+     * @param service The potential receiver.
+     * @param event The event.
+     * @param handledFeedbackTypes The feedback types for which services have been notified.
+     * @return True if the listener should be notified, false otherwise.
+     */
+    private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
+            int handledFeedbackTypes) {
+
+        if (!service.isConfigured()) {
+            return false;
+        }
+
+        if (!service.mService.isBinderAlive()) {
+            removeDeadServiceLocked(service);
+            return false;
+        }
+
+        int eventType = event.getEventType();
+        if ((service.mEventTypes & eventType) != eventType) {
+            return false;
+        }
+
+        Set<String> packageNames = service.mPackageNames;
+        CharSequence packageName = event.getPackageName();
+
+        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
+            int feedbackType = service.mFeedbackType;
+            if ((handledFeedbackTypes & feedbackType) != feedbackType
+                    || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Manages services by starting enabled ones and stopping disabled ones.
+     */
+    private void manageServicesLocked() {
+        populateEnabledServicesLocked(mEnabledServices);
+        updateServicesStateLocked(mInstalledServices, mEnabledServices);
+    }
+
+    /**
+     * Unbinds all bound services.
+     */
+    private void unbindAllServicesLocked() {
+        List<Service> services = mServices;
+
+        for (int i = 0, count = services.size(); i < count; i++) {
+            Service service = services.get(i);
+
+            service.unbind();
+            mComponentNameToServiceMap.remove(service.mComponentName);
+        }
+        services.clear();
+    }
+
+    /**
+     * Populates a list with the {@link ComponentName}s of all enabled
+     * {@link AccessibilityService}s.
+     *
+     * @param enabledServices The list.
+     */
+    private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
+        enabledServices.clear();
+
+        String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+
+        if (servicesValue != null) {
+            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(servicesValue);
+            while (splitter.hasNext()) {
+                ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
+                enabledServices.add(enabledService);
+            }
+        }
+    }
+
+    /**
+     * Updates the state of each service by starting (or keeping running) enabled ones and
+     * stopping the rest.
+     *
+     * @param installedServices All installed {@link AccessibilityService}s.
+     * @param enabledServices The {@link ComponentName}s of the enabled services.
+     */
+    private void updateServicesStateLocked(List<ServiceInfo> installedServices,
+            Set<ComponentName> enabledServices) {
+
+        Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
+        List<Service> services = mServices;
+
+        for (int i = 0, count = installedServices.size(); i < count; i++) {
+            ServiceInfo intalledService = installedServices.get(i);
+            ComponentName componentName = new ComponentName(intalledService.packageName,
+                    intalledService.name);
+            Service service = componentNameToServiceMap.get(componentName);
+
+            if (enabledServices.contains(componentName)) {
+                if (service == null) {
+                    new Service(componentName).bind();
+                }
+            } else {
+                if (service != null) {
+                    service.unbind();
+                    componentNameToServiceMap.remove(componentName);
+                    services.remove(service);
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
+     */
+    private void updateClientsLocked() {
+        for (int i = 0, count = mClients.size(); i < count; i++) {
+            try {
+                mClients.get(i).setEnabled(mIsEnabled);
+            } catch (RemoteException re) {
+                mClients.remove(i);
+                count--;
+            }
+        }
+    }
+
+    /**
+     * This class represents an accessibility service. It stores all per service
+     * data required for the service management, provides API for starting/stopping the
+     * service and is responsible for adding/removing the service in the data structures
+     * for service management. The class also exposes configuration interface that is
+     * passed to the service it represents as soon it is bound. It also serves as the
+     * connection for the service.
+     */
+    class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
+        int mId = 0;
+
+        IBinder mService;
+
+        IEventListener mServiceInterface;
+
+        int mEventTypes;
+
+        int mFeedbackType;
+
+        Set<String> mPackageNames = new HashSet<String>();
+
+        boolean mIsDefault;
+
+        long mNotificationTimeout;
+
+        boolean mIsActive;
+
+        ComponentName mComponentName;
+
+        Intent mIntent;
+
+        // the events pending events to be dispatched to this service
+        final SparseArray<AccessibilityEvent> mPendingEvents =
+            new SparseArray<AccessibilityEvent>();
+
+        Service(ComponentName componentName) {
+            mId = sIdCounter++;
+            mComponentName = componentName;
+            mIntent = new Intent().setComponent(mComponentName);
+        }
+
+        /**
+         * Binds to the accessibility service.
+         */
+        public void bind() {
+            if (mService == null) {
+                mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
+            }
+        }
+
+        /**
+         * Unbinds form the accessibility service and removes it from the data
+         * structures for service management.
+         */
+        public void unbind() {
+            if (mService != null) {
+                mContext.unbindService(this);
+            }
+        }
+
+        /**
+         * Returns if the service is configured i.e. at least event types of interest
+         * and feedback type must be set.
+         *
+         * @return True if the service is configured, false otherwise.
+         */
+        public boolean isConfigured() {
+            return (mEventTypes != 0 && mFeedbackType != 0);
+        }
+
+        public void setServiceInfo(AccessibilityServiceInfo info) {
+            mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
+        }
+
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            mService = service;
+            mServiceInterface = IEventListener.Stub.asInterface(service);
+
+            try {
+                mServiceInterface.setConnection(this);
+                synchronized (mLock) {
+                    if (!mServices.contains(this)) {
+                        mServices.add(this);
+                        mComponentNameToServiceMap.put(componentName, this);
+                    }
+                }
+            } catch (RemoteException re) {
+                Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName componentName) {
+            synchronized (mLock) {
+                Service service = mComponentNameToServiceMap.remove(componentName);
+                mServices.remove(service);
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index c50ae94..131e156 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -29,7 +29,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
-import android.content.pm.PackageItemInfo;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.net.Uri;
@@ -40,6 +39,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.widget.RemoteViews;
 
@@ -56,7 +56,6 @@
 
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.appwidget.IAppWidgetHost;
-import com.android.internal.util.XmlUtils;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -79,7 +78,7 @@
     static class Provider {
         int uid;
         AppWidgetProviderInfo info;
-        ArrayList<AppWidgetId> instances = new ArrayList();
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
         PendingIntent broadcast;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
         
@@ -90,7 +89,7 @@
         int uid;
         int hostId;
         String packageName;
-        ArrayList<AppWidgetId> instances = new ArrayList();
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
         IAppWidgetHost callbacks;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
         
@@ -107,10 +106,10 @@
     Context mContext;
     PackageManager mPackageManager;
     AlarmManager mAlarmManager;
-    ArrayList<Provider> mInstalledProviders = new ArrayList();
+    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
     int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
-    ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList();
-    ArrayList<Host> mHosts = new ArrayList();
+    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+    ArrayList<Host> mHosts = new ArrayList<Host>();
     boolean mSafeMode;
 
     AppWidgetService(Context context) {
@@ -174,7 +173,7 @@
             for (int i=0; i<N; i++) {
                 AppWidgetId id = mAppWidgetIds.get(i);
                 pw.print("  ["); pw.print(i); pw.print("] id=");
-                        pw.println(id.appWidgetId);;
+                        pw.println(id.appWidgetId);
                 pw.print("    hostId=");
                         pw.print(id.host.hostId); pw.print(' ');
                         pw.print(id.host.packageName); pw.print('/');
@@ -384,7 +383,7 @@
     public List<AppWidgetProviderInfo> getInstalledProviders() {
         synchronized (mAppWidgetIds) {
             final int N = mInstalledProviders.size();
-            ArrayList<AppWidgetProviderInfo> result = new ArrayList(N);
+            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
             for (int i=0; i<N; i++) {
                 Provider p = mInstalledProviders.get(i);
                 if (!p.zombie) {
@@ -619,7 +618,6 @@
             // rely on the fact that we've already set it and that
             // PendingIntent.getBroadcast will update the extras.
             boolean alreadyRegistered = p.broadcast != null;
-            int instancesSize = p.instances.size();
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
             intent.setComponent(p.info.provider);
@@ -695,10 +693,16 @@
 
             TypedArray sa = mContext.getResources().obtainAttributes(attrs,
                     com.android.internal.R.styleable.AppWidgetProviderInfo);
-            info.minWidth = sa.getDimensionPixelSize(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0);
-            info.minHeight = sa.getDimensionPixelSize(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0);
+            
+            // These dimensions has to be resolved in the application's context.
+            // We simply send back the raw complex data, which will be
+            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+            TypedValue value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+            info.minWidth = value != null ? value.data : 0; 
+            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+            info.minHeight = value != null ? value.data : 0;
+                    
             info.updatePeriodMillis = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
             info.initialLayout = sa.getResourceId(
@@ -773,10 +777,12 @@
         if (real.exists()) {
             readStateFromFileLocked(real);
             if (temp.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 temp.delete();
             }
         } else if (temp.exists()) {
             readStateFromFileLocked(temp);
+            //noinspection ResultOfMethodCallIgnored
             temp.renameTo(real);
         }
     }
@@ -792,18 +798,23 @@
             // use the temporary one until it's fully written, create an empty file
             // for real, which will we'll shortly delete.
             try {
+                //noinspection ResultOfMethodCallIgnored
                 real.createNewFile();
             } catch (IOException e) {
+                // Ignore
             }
         }
 
         if (temp.exists()) {
+            //noinspection ResultOfMethodCallIgnored
             temp.delete();
         }
 
         writeStateToFileLocked(temp);
 
+        //noinspection ResultOfMethodCallIgnored
         real.delete();
+        //noinspection ResultOfMethodCallIgnored
         temp.renameTo(real);
     }
 
@@ -866,8 +877,10 @@
                     stream.close();
                 }
             } catch (IOException ex) {
+                // Ignore
             }
             if (file.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 file.delete();
             }
         }
@@ -885,7 +898,7 @@
 
             int type;
             int providerIndex = 0;
-            HashMap<Integer,Provider> loadedProviders = new HashMap();
+            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
             do {
                 type = parser.next();
                 if (type == XmlPullParser.START_TAG) {
@@ -986,6 +999,7 @@
                 stream.close();
             }
         } catch (IOException e) {
+            // Ignore
         }
 
         if (success) {
@@ -1081,7 +1095,7 @@
     // TODO: If there's a better way of matching an intent filter against the
     // packages for a given package, use that.
     void updateProvidersForPackageLocked(String pkgName) {
-        HashSet<String> keep = new HashSet();
+        HashSet<String> keep = new HashSet<String>();
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
@@ -1103,7 +1117,6 @@
                     if (parsed != null) {
                         keep.add(ai.name);
                         // Use the new AppWidgetProviderInfo.
-                        AppWidgetProviderInfo oldInfo = p.info;
                         p.info = parsed.info;
                         // If it's enabled
                         final int M = p.instances.size();
diff --git a/services/java/com/android/server/AttributeCache.java b/services/java/com/android/server/AttributeCache.java
index 459ae52..81378dc 100644
--- a/services/java/com/android/server/AttributeCache.java
+++ b/services/java/com/android/server/AttributeCache.java
@@ -17,56 +17,36 @@
 
 package com.android.server;
 
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.provider.Settings;
-import android.util.Config;
-import android.util.Log;
+import android.util.SparseArray;
 
+import java.util.HashMap;
 import java.util.WeakHashMap;
 
-public final class AttributeCache extends BroadcastReceiver {
+/**
+ * TODO: This should be better integrated into the system so it doesn't need
+ * special calls from the activity manager to clear it.
+ */
+public final class AttributeCache {
     private static AttributeCache sInstance = null;
     
     private final Context mContext;
-    private final WeakHashMap<Key, Entry> mMap =
-            new WeakHashMap<Key, Entry>();
-    private final WeakHashMap<String, Context> mContexts =
-            new WeakHashMap<String, Context>();
+    private final WeakHashMap<String, Package> mPackages =
+            new WeakHashMap<String, Package>();
+    private final Configuration mConfiguration = new Configuration();
     
-    final static class Key {
-        public final String packageName;
-        public final int resId;
-        public final int[] styleable;
+    public final static class Package {
+        public final Context context;
+        private final SparseArray<HashMap<int[], Entry>> mMap
+                = new SparseArray<HashMap<int[], Entry>>();
         
-        public Key(String inPackageName, int inResId, int[] inStyleable) {
-            packageName = inPackageName;
-            resId = inResId;
-            styleable = inStyleable;
-        }
-        
-        @Override public boolean equals(Object obj) {
-            try {
-                if (obj != null) {
-                    Key other = (Key)obj;
-                    return packageName.equals(other.packageName)
-                            && resId == other.resId
-                            && styleable == other.styleable;
-                }
-            } catch (ClassCastException e) {
-            }
-            return false;
-        }
-
-        @Override public int hashCode() {
-            return packageName.hashCode() + resId;
+        public Package(Context c) {
+            context = c;
         }
     }
     
@@ -94,36 +74,68 @@
         mContext = context;
     }
     
+    public void removePackage(String packageName) {
+        synchronized (this) {
+            mPackages.remove(packageName);
+        }
+    }
+    
+    public void updateConfiguration(Configuration config) {
+        synchronized (this) {
+            int changes = mConfiguration.updateFrom(config);
+            if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE |
+                    ActivityInfo.CONFIG_KEYBOARD_HIDDEN |
+                    ActivityInfo.CONFIG_ORIENTATION)) != 0) {
+                // The configurations being masked out are ones that commonly
+                // change so we don't want flushing the cache... all others
+                // will flush the cache.
+                mPackages.clear();
+            }
+        }
+    }
+    
     public Entry get(String packageName, int resId, int[] styleable) {
         synchronized (this) {
-            Key key = new Key(packageName, resId, styleable);
-            Entry ent = mMap.get(key);
-            if (ent != null) {
-                return ent;
-            }
-            Context context = mContexts.get(packageName);
-            if (context == null) {
+            Package pkg = mPackages.get(packageName);
+            HashMap<int[], Entry> map = null;
+            Entry ent = null;
+            if (pkg != null) {
+                map = pkg.mMap.get(resId);
+                if (map != null) {
+                    ent = map.get(styleable);
+                    if (ent != null) {
+                        return ent;
+                    }
+                }
+            } else {
+                Context context;
                 try {
                     context = mContext.createPackageContext(packageName, 0);
                     if (context == null) {
                         return null;
                     }
-                    mContexts.put(packageName, context);
                 } catch (PackageManager.NameNotFoundException e) {
                     return null;
                 }
+                pkg = new Package(context);
+                mPackages.put(packageName, pkg);
             }
+            
+            if (map == null) {
+                map = new HashMap<int[], Entry>();
+                pkg.mMap.put(resId, map);
+            }
+            
             try {
-                ent = new Entry(context,
-                        context.obtainStyledAttributes(resId, styleable));
-                mMap.put(key, ent);
+                ent = new Entry(pkg.context,
+                        pkg.context.obtainStyledAttributes(resId, styleable));
+                map.put(styleable, ent);
             } catch (Resources.NotFoundException e) {
                 return null;
             }
+            
             return ent;
         }
     }
-    @Override public void onReceive(Context context, Intent intent) {
-    }
 }
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 983329b..3b82284 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -16,18 +16,26 @@
 
 package com.android.server;
 
-import android.backup.BackupService;
-import android.backup.IBackupService;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
+import android.app.IBackupAgent;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
 import android.net.Uri;
+import android.provider.Settings;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -35,71 +43,212 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
+import android.backup.IRestoreSession;
+import android.backup.RestoreSet;
 
+import com.android.internal.backup.LocalTransport;
+import com.android.internal.backup.IBackupTransport;
+
+import com.android.server.PackageManagerBackupAgent;
+import com.android.server.PackageManagerBackupAgent.Metadata;
+
+import java.io.EOFException;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.RandomAccessFile;
 import java.lang.String;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 
 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;
 
+    // How often we perform a backup pass.  Privileged external callers can
+    // trigger an immediate pass.
+    private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
+
+    // The amount of time between the initial provisioning of the device and
+    // the first backup pass.
+    private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
+
+    private static final String RUN_BACKUP_ACTION = "_backup_run_";
     private static final int MSG_RUN_BACKUP = 1;
-    
+    private static final int MSG_RUN_FULL_BACKUP = 2;
+    private static final int MSG_RUN_RESTORE = 3;
+    private static final int MSG_RUN_CLEAR = 4;
+
+    // Timeout interval for deciding that a bind or clear-data has taken too long
+    static final long TIMEOUT_INTERVAL = 10 * 1000;
+
     private Context mContext;
     private PackageManager mPackageManager;
+    private IActivityManager mActivityManager;
+    private PowerManager mPowerManager;
+    private AlarmManager mAlarmManager;
+
+    private boolean mEnabled;   // access to this is synchronized on 'this'
+    private boolean mProvisioned;
+    private PowerManager.WakeLock mWakelock;
     private final BackupHandler mBackupHandler = new BackupHandler();
+    private PendingIntent mRunBackupIntent;
+    private BroadcastReceiver mRunBackupReceiver;
+    private IntentFilter mRunBackupFilter;
     // map UIDs to the set of backup client services within that UID's app set
-    private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
-        = new SparseArray<HashSet<ServiceInfo>>();
+    private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+        = new SparseArray<HashSet<ApplicationInfo>>();
     // set of backup services that have pending changes
     private class BackupRequest {
-        public ServiceInfo service;
+        public ApplicationInfo appInfo;
         public boolean fullBackup;
-        
-        BackupRequest(ServiceInfo svc, boolean isFull) {
-            service = svc;
+
+        BackupRequest(ApplicationInfo app, boolean isFull) {
+            appInfo = app;
             fullBackup = isFull;
         }
+
+        public String toString() {
+            return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
+        }
     }
     // 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 HashMap<ApplicationInfo,BackupRequest> mPendingBackups
+            = new HashMap<ApplicationInfo,BackupRequest>();
+
+    // Pseudoname that we use for the Package Manager metadata "package"
+    private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+
+    // locking around the pending-backup management
     private final Object mQueueLock = new Object();
 
-    private File mStateDir;
+    // The thread performing the sequence of queued backups binds to each app's agent
+    // in succession.  Bind notifications are asynchronously delivered through the
+    // Activity Manager; use this lock object to signal when a requested binding has
+    // completed.
+    private final Object mAgentConnectLock = new Object();
+    private IBackupAgent mConnectedAgent;
+    private volatile boolean mConnecting;
+
+    // A similar synchronicity mechanism around clearing apps' data for restore
+    private final Object mClearDataLock = new Object();
+    private volatile boolean mClearingData;
+
+    // Transport bookkeeping
+    private final HashMap<String,IBackupTransport> mTransports
+            = new HashMap<String,IBackupTransport>();
+    private String mCurrentTransport;
+    private IBackupTransport mLocalTransport, mGoogleTransport;
+    private RestoreSession mActiveRestoreSession;
+
+    private class RestoreParams {
+        public IBackupTransport transport;
+        public IRestoreObserver observer;
+        public long token;
+
+        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
+            transport = _transport;
+            observer = _obs;
+            token = _token;
+        }
+    }
+
+    private class ClearParams {
+        public IBackupTransport transport;
+        public PackageInfo packageInfo;
+
+        ClearParams(IBackupTransport _transport, PackageInfo _info) {
+            transport = _transport;
+            packageInfo = _info;
+        }
+    }
+
+    // Where we keep our journal files and other bookkeeping
+    private File mBaseStateDir;
     private File mDataDir;
-    
+    private File mJournalDir;
+    private File mJournal;
+    private RandomAccessFile mJournalStream;
+
     public BackupManagerService(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
+        mActivityManager = ActivityManagerNative.getDefault();
+
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
 
         // Set up our bookkeeping
-        mStateDir = new File(Environment.getDataDirectory(), "backup");
-        mStateDir.mkdirs();
+        boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.BACKUP_ENABLED, 0) != 0;
+        // !!! TODO: mProvisioned needs to default to 0, not 1.
+        mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.BACKUP_PROVISIONED, 1) != 0;
+        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
         mDataDir = Environment.getDownloadCacheDirectory();
-        
-        // Build our mapping of uid to backup client services
+
+        mRunBackupReceiver = new RunBackupReceiver();
+        mRunBackupFilter = new IntentFilter();
+        mRunBackupFilter.addAction(RUN_BACKUP_ACTION);
+        context.registerReceiver(mRunBackupReceiver, mRunBackupFilter);
+
+        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+        // !!! TODO: restrict delivery to our receiver; the naive setClass() doesn't seem to work
+        //backupIntent.setClass(context, mRunBackupReceiver.getClass());
+        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
+
+        // Set up the backup-request journaling
+        mJournalDir = new File(mBaseStateDir, "pending");
+        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
+        makeJournalLocked();    // okay because no other threads are running yet
+
+        // Build our mapping of uid to backup client services.  This implicitly
+        // schedules a backup pass on the Package Manager metadata the first
+        // time anything needs to be backed up.
         synchronized (mBackupParticipants) {
             addPackageParticipantsLocked(null);
         }
 
+        // Set up our transport options and initialize the default transport
+        // TODO: Have transports register themselves somehow?
+        // TODO: Don't create transports that we don't need to?
+        mLocalTransport = new LocalTransport(context);  // This is actually pretty cheap
+        ComponentName localName = new ComponentName(context, LocalTransport.class);
+        registerTransport(localName.flattenToShortString(), mLocalTransport);
+
+        mGoogleTransport = null;
+        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT);
+        if ("".equals(mCurrentTransport)) {
+            mCurrentTransport = null;
+        }
+        if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
+
+        // Attach to the Google backup transport.  When this comes up, it will set
+        // itself as the current transport because we explicitly reset mCurrentTransport
+        // to null.
+        Intent intent = new Intent().setComponent(new ComponentName(
+                "com.google.android.backup",
+                "com.google.android.backup.BackupTransportService"));
+        context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
+
+        // Now that we know about valid backup participants, parse any
+        // leftover journal files into the pending backup set
+        parseLeftoverJournals();
+
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
         IntentFilter filter = new IntentFilter();
@@ -107,6 +256,77 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mBroadcastReceiver, filter);
+
+        // Power management
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
+
+        // Start the backup passes going
+        setBackupEnabled(areEnabled);
+    }
+
+    private class RunBackupReceiver extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
+                if (DEBUG) Log.v(TAG, "Running a backup pass");
+
+                synchronized (mQueueLock) {
+                    // acquire a wakelock and pass it to the backup thread.  it will
+                    // be released once backup concludes.
+                    mWakelock.acquire();
+
+                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+                    mBackupHandler.sendMessage(msg);
+                }
+            }
+        }
+    }
+
+    private void makeJournalLocked() {
+        try {
+            mJournal = File.createTempFile("journal", null, mJournalDir);
+            mJournalStream = new RandomAccessFile(mJournal, "rwd");
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write backup journals");
+            mJournal = null;
+            mJournalStream = null;
+        }
+    }
+
+    private void parseLeftoverJournals() {
+        if (mJournal != null) {
+            File[] allJournals = mJournalDir.listFiles();
+            for (File f : allJournals) {
+                if (f.compareTo(mJournal) != 0) {
+                    // This isn't the current journal, so it must be a leftover.  Read
+                    // out the package names mentioned there and schedule them for
+                    // backup.
+                    try {
+                        Log.i(TAG, "Found stale backup journal, scheduling:");
+                        RandomAccessFile in = new RandomAccessFile(f, "r");
+                        while (true) {
+                            String packageName = in.readUTF();
+                            Log.i(TAG, "    + " + packageName);
+                            dataChanged(packageName);
+                        }
+                    } catch (EOFException e) {
+                        // no more data; we're done
+                    } catch (Exception e) {
+                        // can't read it or other error; just skip it
+                    } finally {
+                        // close/delete the file
+                        f.delete();
+                    }
+                }
+            }
+        }
+    }
+
+    // Add a transport to our set of available backends
+    private void registerTransport(String name, IBackupTransport transport) {
+        synchronized (mTransports) {
+            if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport);
+            mTransports.put(name, transport);
+        }
     }
 
     // ----- Track installation/removal of packages -----
@@ -149,196 +369,192 @@
         }
     };
 
+    // ----- Track connection to GoogleBackupTransport service -----
+    ServiceConnection mGoogleConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Log.v(TAG, "Connected to Google transport");
+            mGoogleTransport = IBackupTransport.Stub.asInterface(service);
+            registerTransport(name.flattenToShortString(), mGoogleTransport);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Log.v(TAG, "Disconnected from Google transport");
+            mGoogleTransport = null;
+            registerTransport(name.flattenToShortString(), null);
+        }
+    };
+
     // ----- Run the actual backup process asynchronously -----
 
-    private class BackupHandler extends Handler implements ServiceConnection {
+    private class BackupHandler extends Handler {
         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.
+            {
+                IBackupTransport transport = getTransport(mCurrentTransport);
+                if (transport == null) {
+                    Log.v(TAG, "Backup requested but no transport available");
+                    mWakelock.release();
+                    break;
                 }
-                startOneService();
+
+                // snapshot the pending-backup set and work on that
+                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
+                File oldJournal = mJournal;
+                synchronized (mQueueLock) {
+                    // Do we have any work to do?
+                    if (mPendingBackups.size() > 0) {
+                        for (BackupRequest b: mPendingBackups.values()) {
+                            queue.add(b);
+                        }
+                        Log.v(TAG, "clearing pending backups");
+                        mPendingBackups.clear();
+
+                        // Start a new backup-queue journal file too
+                        if (mJournalStream != null) {
+                            try {
+                                mJournalStream.close();
+                            } catch (IOException e) {
+                                // don't need to do anything
+                            }
+                            makeJournalLocked();
+                        }
+
+                        // At this point, we have started a new journal file, and the old
+                        // file identity is being passed to the backup processing thread.
+                        // When it completes successfully, that old journal file will be
+                        // deleted.  If we crash prior to that, the old journal is parsed
+                        // at next boot and the journaled requests fulfilled.
+                        (new PerformBackupThread(transport, queue, oldJournal)).start();
+                    } else {
+                        Log.v(TAG, "Backup requested but nothing pending");
+                        mWakelock.release();
+                    }
+                }
                 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
-        }
-    }
+            case MSG_RUN_FULL_BACKUP:
+                break;
 
-    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.
+            case MSG_RUN_RESTORE:
+            {
+                RestoreParams params = (RestoreParams)msg.obj;
+                Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
+                (new PerformRestoreThread(params.transport, params.observer,
+                        params.token)).start();
+                break;
             }
-            
-            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);
+
+            case MSG_RUN_CLEAR:
+            {
+                ClearParams params = (ClearParams)msg.obj;
+                (new PerformClearThread(params.transport, params.packageInfo)).start();
+                break;
+            }
             }
         }
     }
 
-    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.
+    // Add the backup agents in the given package to our set of known backup participants.
+    // If 'packageName' is null, adds all backup agents in the whole system.
     void addPackageParticipantsLocked(String packageName) {
-        List<ResolveInfo> services = mPackageManager.queryIntentServices(
-                new Intent(BackupService.SERVICE_ACTION), 0);
-        addPackageParticipantsLockedInner(packageName, services);
+        // Look for apps that define the android:backupAgent attribute
+        if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
+        List<PackageInfo> targetApps = allAgentPackages();
+        addPackageParticipantsLockedInner(packageName, targetApps);
     }
 
-    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);
+    private void addPackageParticipantsLockedInner(String packageName,
+            List<PackageInfo> targetPkgs) {
+        if (DEBUG) {
+            Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
+            for (PackageInfo p : targetPkgs) {
+                Log.v(TAG, "    " + p + " agent=" + p.applicationInfo.backupAgentName
+                        + " uid=" + p.applicationInfo.uid);
+            }
+        }
+
+        for (PackageInfo pkg : targetPkgs) {
+            if (packageName == null || pkg.packageName.equals(packageName)) {
+                int uid = pkg.applicationInfo.uid;
+                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
                 if (set == null) {
-                    set = new HashSet<ServiceInfo>();
+                    set = new HashSet<ApplicationInfo>();
                     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);
+                set.add(pkg.applicationInfo);
             }
         }
     }
 
-    // Remove the given package's backup services from our known active set.  If
-    // 'packageName' is null, *all* backup services will be removed.
+    // Remove the given package's entry from our known active set.  If
+    // 'packageName' is null, *all* participating apps will be removed.
     void removePackageParticipantsLocked(String packageName) {
-        List<ResolveInfo> services = mPackageManager.queryIntentServices(
-                new Intent(BackupService.SERVICE_ACTION), 0);
-        removePackageParticipantsLockedInner(packageName, services);
+        if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
+        List<PackageInfo> allApps = null;
+        if (packageName != null) {
+            allApps = new ArrayList<PackageInfo>();
+            try {
+                int flags = PackageManager.GET_SIGNATURES;
+                allApps.add(mPackageManager.getPackageInfo(packageName, flags));
+            } catch (Exception e) {
+                // just skip it (???)
+            }
+        } else {
+            // all apps with agents
+            allApps = allAgentPackages();
+        }
+        removePackageParticipantsLockedInner(packageName, allApps);
     }
 
     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);
+            List<PackageInfo> agents) {
+        if (DEBUG) {
+            Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
+                    + ") removing " + agents.size() + " entries");
+            for (PackageInfo p : agents) {
+                Log.v(TAG, "    - " + p);
+            }
+        }
+        for (PackageInfo pkg : agents) {
+            if (packageName == null || pkg.packageName.equals(packageName)) {
+                int uid = pkg.applicationInfo.uid;
+                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
                 if (set != null) {
-                    set.remove(ri.serviceInfo);
+                    // Find the existing entry with the same package name, and remove it.
+                    // We can't just remove(app) because the instances are different.
+                    for (ApplicationInfo entry: set) {
+                        if (entry.packageName.equals(pkg.packageName)) {
+                            set.remove(entry);
+                            break;
+                        }
+                    }
                     if (set.size() == 0) {
-                        mBackupParticipants.put(uid, null);
+                        mBackupParticipants.delete(uid);
                     }
                 }
             }
         }
     }
 
+    // Returns the set of all applications that define an android:backupAgent attribute
+    private List<PackageInfo> allAgentPackages() {
+        // !!! TODO: cache this and regenerate only when necessary
+        int flags = PackageManager.GET_SIGNATURES;
+        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+        int N = packages.size();
+        for (int a = N-1; a >= 0; a--) {
+            ApplicationInfo app = packages.get(a).applicationInfo;
+            if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+                    || app.backupAgentName == null) {
+                packages.remove(a);
+            }
+        }
+        return packages;
+    }
+
     // 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) {
@@ -346,85 +562,1008 @@
             Log.e(TAG, "updatePackageParticipants called with null package name");
             return;
         }
+        if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
 
         // brute force but small code size
-        List<ResolveInfo> services = mPackageManager.queryIntentServices(
-                new Intent(BackupService.SERVICE_ACTION), 0);
-        removePackageParticipantsLockedInner(packageName, services);
-        addPackageParticipantsLockedInner(packageName, services);
+        List<PackageInfo> allApps = allAgentPackages();
+        removePackageParticipantsLockedInner(packageName, allApps);
+        addPackageParticipantsLockedInner(packageName, allApps);
     }
 
+    // Return the given transport
+    private IBackupTransport getTransport(String transportName) {
+        synchronized (mTransports) {
+            IBackupTransport transport = mTransports.get(transportName);
+            if (transport == null) {
+                Log.w(TAG, "Requested unavailable transport: " + transportName);
+            }
+            return transport;
+        }
+    }
+
+    // fire off a backup agent, blocking until it attaches or times out
+    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
+        IBackupAgent agent = null;
+        synchronized(mAgentConnectLock) {
+            mConnecting = true;
+            mConnectedAgent = null;
+            try {
+                if (mActivityManager.bindBackupAgent(app, mode)) {
+                    Log.d(TAG, "awaiting agent for " + app);
+
+                    // success; wait for the agent to arrive
+                    // only wait 10 seconds for the clear data to happen
+                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+                    while (mConnecting && mConnectedAgent == null
+                            && (System.currentTimeMillis() < timeoutMark)) {
+                        try {
+                            mAgentConnectLock.wait(5000);
+                        } catch (InterruptedException e) {
+                            // just bail
+                            return null;
+                        }
+                    }
+
+                    // if we timed out with no connect, abort and move on
+                    if (mConnecting == true) {
+                        Log.w(TAG, "Timeout waiting for agent " + app);
+                        return null;
+                    }
+                    agent = mConnectedAgent;
+                }
+            } catch (RemoteException e) {
+                // can't happen
+            }
+        }
+        return agent;
+    }
+
+    // clear an application's data, blocking until the operation completes or times out
+    void clearApplicationDataSynchronous(String packageName) {
+        // Don't wipe packages marked allowClearUserData=false
+        try {
+            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+                if (DEBUG) Log.i(TAG, "allowClearUserData=false so not wiping "
+                        + packageName);
+                return;
+            }
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Tried to clear data for " + packageName + " but not found");
+            return;
+        }
+
+        ClearDataObserver observer = new ClearDataObserver();
+
+        synchronized(mClearDataLock) {
+            mClearingData = true;
+            mPackageManager.clearApplicationUserData(packageName, observer);
+
+            // only wait 10 seconds for the clear data to happen
+            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+                try {
+                    mClearDataLock.wait(5000);
+                } catch (InterruptedException e) {
+                    // won't happen, but still.
+                    mClearingData = false;
+                }
+            }
+        }
+    }
+
+    class ClearDataObserver extends IPackageDataObserver.Stub {
+        public void onRemoveCompleted(String packageName, boolean succeeded)
+                throws android.os.RemoteException {
+            synchronized(mClearDataLock) {
+                mClearingData = false;
+                mClearDataLock.notifyAll();
+            }
+        }
+    }
+
+    // ----- Back up a set of applications via a worker thread -----
+
+    class PerformBackupThread extends Thread {
+        private static final String TAG = "PerformBackupThread";
+        IBackupTransport mTransport;
+        ArrayList<BackupRequest> mQueue;
+        File mStateDir;
+        File mJournal;
+
+        public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
+                File journal) {
+            mTransport = transport;
+            mQueue = queue;
+            mJournal = journal;
+
+            try {
+                mStateDir = new File(mBaseStateDir, transport.transportDirName());
+            } catch (RemoteException e) {
+                // can't happen; the transport is local
+            }
+            mStateDir.mkdirs();
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
+
+            // Backups run at background priority
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+            // The package manager doesn't have a proper <application> etc, but since
+            // it's running here in the system process we can just set up its agent
+            // directly and use a synthetic BackupRequest.  We always run this pass
+            // because it's cheap and this way we guarantee that we don't get out of
+            // step even if we're selecting among various transports at run time.
+            PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                    mPackageManager, allAgentPackages());
+            BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+            pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+            processOneBackup(pmRequest,
+                    IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+                    mTransport);
+
+            // Now run all the backups in our queue
+            doQueuedBackups(mTransport);
+
+            // Finally, tear down the transport
+            try {
+                if (!mTransport.finishBackup()) {
+                    // STOPSHIP TODO: handle errors
+                    Log.e(TAG, "Backup failure in finishBackup()");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in finishBackup()", e);
+            }
+
+            if (!mJournal.delete()) {
+                Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath());
+            }
+
+            // Only once we're entirely finished do we release the wakelock
+            mWakelock.release();
+        }
+
+        private void doQueuedBackups(IBackupTransport transport) {
+            for (BackupRequest request : mQueue) {
+                Log.d(TAG, "starting agent for backup of " + request);
+
+                IBackupAgent agent = null;
+                int mode = (request.fullBackup)
+                        ? IApplicationThread.BACKUP_MODE_FULL
+                        : IApplicationThread.BACKUP_MODE_INCREMENTAL;
+                try {
+                    agent = bindToAgentSynchronous(request.appInfo, mode);
+                    if (agent != null) {
+                        processOneBackup(request, agent, transport);
+                    }
+
+                    // unbind even on timeout, just in case
+                    mActivityManager.unbindBackupAgent(request.appInfo);
+                } catch (SecurityException ex) {
+                    // Try for the next one.
+                    Log.d(TAG, "error in bind/backup", ex);
+                } catch (RemoteException e) {
+                    Log.v(TAG, "bind/backup threw");
+                    e.printStackTrace();
+                }
+
+            }
+        }
+
+        void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
+            final String packageName = request.appInfo.packageName;
+            Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+
+            try {
+                // Look up the package info & signatures.  This is first so that if it
+                // throws an exception, there's no file setup yet that would need to
+                // be unraveled.
+                PackageInfo packInfo;
+                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+                    // The metadata 'package' is synthetic
+                    packInfo = new PackageInfo();
+                    packInfo.packageName = packageName;
+                } else {
+                    packInfo = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_SIGNATURES);
+                }
+
+                // !!! TODO: get the state file dir from the transport
+                File savedStateName = new File(mStateDir, packageName);
+                File backupDataName = new File(mDataDir, packageName + ".data");
+                File newStateName = new File(mStateDir, 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
+                boolean success = false;
+                try {
+                    agent.doBackup(savedState, backupData, newState);
+                    success = true;
+                } finally {
+                    if (savedState != null) {
+                        savedState.close();
+                    }
+                    backupData.close();
+                    newState.close();
+                }
+
+                // Now propagate the newly-backed-up data to the transport
+                if (success) {
+                    if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
+                    backupData =
+                        ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
+                    if (!transport.performBackup(packInfo, backupData)) {
+                        // STOPSHIP TODO: handle errors
+                        Log.e(TAG, "Backup failure in performBackup()");
+                    }
+
+                    // !!! 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 (Exception e) {
+                Log.e(TAG, "Error backing up " + packageName, e);
+            }
+        }
+    }
+
+
+    // ----- Restore handling -----
+
+    private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
+        // Allow unsigned apps, but not signed on one device and unsigned on the other
+        // !!! TODO: is this the right policy?
+        if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
+                + " device=" + deviceSigs);
+        if ((storedSigs == null || storedSigs.length == 0)
+                && (deviceSigs == null || deviceSigs.length == 0)) {
+            return true;
+        }
+        if (storedSigs == null || deviceSigs == null) {
+            return false;
+        }
+
+        // !!! TODO: this demands that every stored signature match one
+        // that is present on device, and does not demand the converse.
+        // Is this this right policy?
+        int nStored = storedSigs.length;
+        int nDevice = deviceSigs.length;
+
+        for (int i=0; i < nStored; i++) {
+            boolean match = false;
+            for (int j=0; j < nDevice; j++) {
+                if (storedSigs[i].equals(deviceSigs[j])) {
+                    match = true;
+                    break;
+                }
+            }
+            if (!match) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    class PerformRestoreThread extends Thread {
+        private IBackupTransport mTransport;
+        private IRestoreObserver mObserver;
+        private long mToken;
+        private RestoreSet mImage;
+        private File mStateDir;
+
+        class RestoreRequest {
+            public PackageInfo app;
+            public int storedAppVersion;
+
+            RestoreRequest(PackageInfo _app, int _version) {
+                app = _app;
+                storedAppVersion = _version;
+            }
+        }
+
+        PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+                long restoreSetToken) {
+            mTransport = transport;
+            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
+            mObserver = observer;
+            mToken = restoreSetToken;
+
+            try {
+                mStateDir = new File(mBaseStateDir, transport.transportDirName());
+            } catch (RemoteException e) {
+                // can't happen; the transport is local
+            }
+            mStateDir.mkdirs();
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
+                    + " mObserver=" + mObserver + " mToken=" + mToken);
+            /**
+             * Restore sequence:
+             *
+             * 1. get the restore set description for our identity
+             * 2. for each app in the restore set:
+             *    3.a. if it's restorable on this device, add it to the restore queue
+             * 3. for each app in the restore queue:
+             *    3.a. clear the app data
+             *    3.b. get the restore data for the app from the transport
+             *    3.c. launch the backup agent for the app
+             *    3.d. agent.doRestore() with the data from the server
+             *    3.e. unbind the agent [and kill the app?]
+             * 4. shut down the transport
+             */
+
+            int error = -1; // assume error
+
+            // build the set of apps to restore
+            try {
+                RestoreSet[] images = mTransport.getAvailableRestoreSets();
+                if (images == null) {
+                    // STOPSHIP TODO: Handle the failure somehow?
+                    Log.e(TAG, "Error getting restore sets");
+                    return;
+                }
+
+                if (images.length == 0) {
+                    Log.i(TAG, "No restore sets available");
+                    return;
+                }
+
+                mImage = images[0];
+
+                // Get the list of all packages which have backup enabled.
+                // (Include the Package Manager metadata pseudo-package first.)
+                ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
+                PackageInfo omPackage = new PackageInfo();
+                omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+                restorePackages.add(omPackage);
+
+                List<PackageInfo> agentPackages = allAgentPackages();
+                restorePackages.addAll(agentPackages);
+
+                // let the observer know that we're running
+                if (mObserver != null) {
+                    try {
+                        // !!! TODO: get an actual count from the transport after
+                        // its startRestore() runs?
+                        mObserver.restoreStarting(restorePackages.size());
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreStarting");
+                        mObserver = null;
+                    }
+                }
+
+                if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
+                    // STOPSHIP TODO: Handle the failure somehow?
+                    Log.e(TAG, "Error starting restore operation");
+                    return;
+                }
+
+                String packageName = mTransport.nextRestorePackage();
+                if (packageName == null) {
+                    // STOPSHIP TODO: Handle the failure somehow?
+                    Log.e(TAG, "Error getting first restore package");
+                    return;
+                } else if (packageName.equals("")) {
+                    Log.i(TAG, "No restore data available");
+                    return;
+                } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+                    Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+                          + "\", found only \"" + packageName + "\"");
+                    return;
+                }
+
+                // Pull the Package Manager metadata from the restore set first
+                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                        mPackageManager, agentPackages);
+                processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
+                // Verify that the backup set includes metadata.  If not, we can't do
+                // signature/version verification etc, so we simply do not proceed with
+                // the restore operation.
+                if (!pmAgent.hasMetadata()) {
+                    Log.i(TAG, "No restore metadata available, so not restoring settings");
+                    return;
+                }
+
+                int count = 0;
+                for (;;) {
+                    packageName = mTransport.nextRestorePackage();
+                    if (packageName == null) {
+                        // STOPSHIP TODO: Handle the failure somehow?
+                        Log.e(TAG, "Error getting next restore package");
+                        return;
+                    } else if (packageName.equals("")) {
+                        break;
+                    }
+
+                    if (mObserver != null) {
+                        ++count;
+                        try {
+                            mObserver.onUpdate(count);
+                        } catch (RemoteException e) {
+                            Log.d(TAG, "Restore observer died in onUpdate");
+                            mObserver = null;
+                        }
+                    }
+
+                    Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
+                    if (metaInfo == null) {
+                        Log.e(TAG, "Missing metadata for " + packageName);
+                        continue;
+                    }
+
+                    int flags = PackageManager.GET_SIGNATURES;
+                    PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+                    if (metaInfo.versionCode > packageInfo.versionCode) {
+                        Log.w(TAG, "Package " + packageName
+                                + " restore version [" + metaInfo.versionCode
+                                + "] is too new for installed version ["
+                                + packageInfo.versionCode + "]");
+                        continue;
+                    }
+
+                    if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+                        Log.w(TAG, "Signature mismatch restoring " + packageName);
+                        continue;
+                    }
+
+                    if (DEBUG) Log.v(TAG, "Package " + packageName
+                            + " restore version [" + metaInfo.versionCode
+                            + "] is compatible with installed version ["
+                            + packageInfo.versionCode + "]");
+
+                    // Now perform the actual restore
+                    clearApplicationDataSynchronous(packageName);
+                    IBackupAgent agent = bindToAgentSynchronous(
+                            packageInfo.applicationInfo,
+                            IApplicationThread.BACKUP_MODE_RESTORE);
+                    if (agent == null) {
+                        Log.w(TAG, "Can't find backup agent for " + packageName);
+                        continue;
+                    }
+
+                    try {
+                        processOneRestore(packageInfo, metaInfo.versionCode, agent);
+                    } finally {
+                        // unbind even on timeout or failure, just in case
+                        mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
+                    }
+                }
+
+                // if we get this far, report success to the observer
+                error = 0;
+            } catch (NameNotFoundException e) {
+                // STOPSHIP TODO: Handle the failure somehow?
+                Log.e(TAG, "Invalid paackage restoring data", e);
+            } catch (RemoteException e) {
+                // STOPSHIP TODO: Handle the failure somehow?
+                Log.e(TAG, "Error restoring data", e);
+            } finally {
+                try {
+                    mTransport.finishRestore();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error finishing restore", e);
+                }
+
+                Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
+                if (mObserver != null) {
+                    try {
+                        mObserver.restoreFinished(error);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreFinished");
+                    }
+                }
+
+                // done; we can finally release the wakelock
+                mWakelock.release();
+            }
+        }
+
+        // Do the guts of a restore of one application, using mTransport.getRestoreData().
+        void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
+            // !!! TODO: actually run the restore through mTransport
+            final String packageName = app.packageName;
+
+            Log.d(TAG, "processOneRestore packageName=" + packageName);
+
+            // !!! TODO: get the dirs from the transport
+            File backupDataName = new File(mDataDir, packageName + ".restore");
+            backupDataName.delete();
+            try {
+                ParcelFileDescriptor backupData =
+                    ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+                // Run the transport's restore pass
+                // Run the target's backup pass
+                try {
+                    if (!mTransport.getRestoreData(backupData)) {
+                        // STOPSHIP TODO: Handle this error somehow?
+                        Log.e(TAG, "Error getting restore data for " + packageName);
+                        return;
+                    }
+                } finally {
+                    backupData.close();
+                }
+
+                // Okay, we have the data.  Now have the agent do the restore.
+                File newStateName = new File(mStateDir, packageName + ".new");
+                ParcelFileDescriptor newState =
+                    ParcelFileDescriptor.open(newStateName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+                backupData = ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_ONLY);
+
+                try {
+                    agent.doRestore(backupData, appVersionCode, newState);
+                } finally {
+                    newState.close();
+                    backupData.close();
+                }
+
+                // if everything went okay, remember the recorded state now
+                File savedStateName = new File(mStateDir, packageName);
+                newStateName.renameTo(savedStateName);
+            } catch (Exception e) {
+                Log.e(TAG, "Error restoring data for " + packageName, e);
+            }
+        }
+    }
+
+    class PerformClearThread extends Thread {
+        IBackupTransport mTransport;
+        PackageInfo mPackage;
+
+        PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) {
+            mTransport = transport;
+            mPackage = packageInfo;
+        }
+
+        @Override
+        public void run() {
+            try {
+                // Clear the on-device backup state to ensure a full backup next time
+                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
+                File stateFile = new File(stateDir, mPackage.packageName);
+                stateFile.delete();
+
+                // Tell the transport to remove all the persistent storage for the app
+                mTransport.clearBackupData(mPackage);
+            } catch (RemoteException e) {
+                // can't happen; the transport is local
+            } finally {
+                try {
+                    mTransport.finishBackup();
+                } catch (RemoteException e) {
+                    // can't happen; the transport is local
+                }
+
+                // Last but not least, release the cpu
+                mWakelock.release();
+            }
+        }
+    }
+
+
     // ----- 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 the caller does not hold the BACKUP permission, it can only request a
+        // backup of its own data.
+        HashSet<ApplicationInfo> targets;
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            targets = mBackupParticipants.get(Binder.getCallingUid());
+        } else {
+            // a caller with full permission can ask to back up any participating app
+            // !!! TODO: allow backup of ANY app?
+            targets = new HashSet<ApplicationInfo>();
+            int N = mBackupParticipants.size();
+            for (int i = 0; i < N; i++) {
+                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
+                if (s != null) {
+                    targets.addAll(s);
+                }
+            }
+        }
         if (targets != null) {
             synchronized (mQueueLock) {
                 // Note that this client has made data changes that need to be backed up
-                for (ServiceInfo service : targets) {
+                for (ApplicationInfo app : targets) {
                     // validate the caller-supplied package name against the known set of
                     // packages associated with this uid
-                    if (service.packageName.equals(packageName)) {
+                    if (app.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
+                        BackupRequest req = new BackupRequest(app, false);
+                        if (mPendingBackups.put(app, req) == null) {
+                            // Journal this request in case of crash.  The put()
+                            // operation returned null when this package was not already
+                            // in the set; we want to avoid touching the disk redundantly.
+                            writeToJournalLocked(packageName);
+
+                            if (DEBUG) {
+                                int numKeys = mPendingBackups.size();
+                                Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
+                                for (BackupRequest b : mPendingBackups.values()) {
+                                    Log.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
+                                }
+                            }
+                        }
                     }
                 }
+            }
+        } else {
+            Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'");
+        }
+    }
 
-                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);
+    private void writeToJournalLocked(String str) {
+        if (mJournalStream != null) {
+            try {
+                mJournalStream.writeUTF(str);
+            } catch (IOException e) {
+                Log.e(TAG, "Error writing to backup journal");
+                mJournalStream = null;
+                mJournal = null;
             }
         }
     }
 
-    // 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>();
+    // Clear the given package's backup data from the current transport
+    public void clearBackupData(String packageName) {
+        if (DEBUG) Log.v(TAG, "clearBackupData() of " + packageName);
+        PackageInfo info;
+        try {
+            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+            return;
+        }
+
+        // If the caller does not hold the BACKUP permission, it can only request a
+        // wipe of its own backed-up data.
+        HashSet<ApplicationInfo> apps;
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            apps = mBackupParticipants.get(Binder.getCallingUid());
+        } else {
+            // a caller with full permission can ask to back up any participating app
+            // !!! TODO: allow data-clear of ANY app?
+            if (DEBUG) Log.v(TAG, "Privileged caller, allowing clear of other apps");
+            apps = new HashSet<ApplicationInfo>();
+            int N = mBackupParticipants.size();
+            for (int i = 0; i < N; i++) {
+                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
+                if (s != null) {
+                    apps.addAll(s);
+                }
+            }
+        }
+
+        // now find the given package in the set of candidate apps
+        for (ApplicationInfo app : apps) {
+            if (app.packageName.equals(packageName)) {
+                if (DEBUG) Log.v(TAG, "Found the app - running clear process");
+                // found it; fire off the clear request
+                synchronized (mQueueLock) {
+                    mWakelock.acquire();
+                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
+                            new ClearParams(getTransport(mCurrentTransport), info));
+                    mBackupHandler.sendMessage(msg);
+                }
+                break;
+            }
+        }
+    }
+
+    // Run a backup pass immediately for any applications that have declared
+    // that they have pending updates.
+    public void backupNow() throws RemoteException {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow");
+
+        if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
         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));
+            try {
+                if (DEBUG) Log.v(TAG, "sending immediate backup broadcast");
+                mRunBackupIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+                // should never happen
+                Log.e(TAG, "run-backup intent cancelled!");
+            }
+        }
+    }
+
+    // Enable/disable the backup service
+    public void setBackupEnabled(boolean enable) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupEnabled");
+
+        boolean wasEnabled = mEnabled;
+        synchronized (this) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
+            mEnabled = enable;
+        }
+
+        synchronized (mQueueLock) {
+            if (enable && !wasEnabled && mProvisioned) {
+                // if we've just been enabled, start scheduling backup passes
+                startBackupAlarmsLocked(BACKUP_INTERVAL);
+            } else if (!enable) {
+                // No longer enabled, so stop running backups
+                mAlarmManager.cancel(mRunBackupIntent);
+            }
+        }
+    }
+
+    // Mark the backup service as having been provisioned
+    public void setBackupProvisioned(boolean available) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupProvisioned");
+
+        boolean wasProvisioned = mProvisioned;
+        synchronized (this) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
+            mProvisioned = available;
+        }
+
+        synchronized (mQueueLock) {
+            if (available && !wasProvisioned && mEnabled) {
+                // we're now good to go, so start the backup alarms
+                startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
+            } else if (!available) {
+                // No longer enabled, so stop running backups
+                Log.w(TAG, "Backup service no longer provisioned");
+                mAlarmManager.cancel(mRunBackupIntent);
+            }
+        }
+    }
+
+    private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
+        long when = System.currentTimeMillis() + delayBeforeFirstBackup;
+        mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, when,
+                BACKUP_INTERVAL, mRunBackupIntent);
+    }
+
+    // Report whether the backup mechanism is currently enabled
+    public boolean isBackupEnabled() {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
+        return mEnabled;    // no need to synchronize just to read it
+    }
+
+    // Report the name of the currently active transport
+    public String getCurrentTransport() {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "getCurrentTransport");
+        Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
+        return mCurrentTransport;
+    }
+
+    // Report all known, available backup transports
+    public String[] listAllTransports() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
+
+        String[] list = null;
+        ArrayList<String> known = new ArrayList<String>();
+        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
+            if (entry.getValue() != null) {
+                known.add(entry.getKey());
+            }
+        }
+
+        if (known.size() > 0) {
+            list = new String[known.size()];
+            known.toArray(list);
+        }
+        return list;
+    }
+
+    // Select which transport to use for the next backup operation.  If the given
+    // name is not one of the available transports, no action is taken and the method
+    // returns null.
+    public String selectBackupTransport(String transport) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
+
+        synchronized (mTransports) {
+            String prevTransport = null;
+            if (mTransports.get(transport) != null) {
+                prevTransport = mCurrentTransport;
+                mCurrentTransport = transport;
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_TRANSPORT, transport);
+                Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
+                        + " returning " + prevTransport);
+            } else {
+                Log.w(TAG, "Attempt to select unavailable transport " + transport);
+            }
+            return prevTransport;
+        }
+    }
+
+    // Callback: a requested backup agent has been instantiated.  This should only
+    // be called from the Activity Manager.
+    public void agentConnected(String packageName, IBinder agentBinder) {
+        synchronized(mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
+                mConnectedAgent = agent;
+                mConnecting = false;
+            } else {
+                Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent connected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
+    }
+
+    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
+    // If the agent failed to come up in the first place, the agentBinder argument
+    // will be null.  This should only be called from the Activity Manager.
+    public void agentDisconnected(String packageName) {
+        // TODO: handle backup being interrupted
+        synchronized(mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                mConnectedAgent = null;
+                mConnecting = false;
+            } else {
+                Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent disconnected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
+    }
+
+    // Hand off a restore session
+    public IRestoreSession beginRestoreSession(String transport) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
+
+        synchronized(this) {
+            if (mActiveRestoreSession != null) {
+                Log.d(TAG, "Restore session requested but one already active");
+                return null;
+            }
+            mActiveRestoreSession = new RestoreSession(transport);
+        }
+        return mActiveRestoreSession;
+    }
+
+    // ----- Restore session -----
+
+    class RestoreSession extends IRestoreSession.Stub {
+        private static final String TAG = "RestoreSession";
+
+        private IBackupTransport mRestoreTransport = null;
+        RestoreSet[] mRestoreSets = null;
+
+        RestoreSession(String transport) {
+            mRestoreTransport = getTransport(transport);
+        }
+
+        // --- Binder interface ---
+        public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                    "getAvailableRestoreSets");
+
+            try {
+            synchronized(this) {
+                if (mRestoreSets == null) {
+                    mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
+                }
+                return mRestoreSets;
+            }
+            } catch (RuntimeException e) {
+                Log.d(TAG, "getAvailableRestoreSets exception");
+                e.printStackTrace();
+                throw e;
+            }
+        }
+
+        public int performRestore(long token, IRestoreObserver observer)
+                throws android.os.RemoteException {
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore");
+
+            Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
+
+            if (mRestoreSets != null) {
+                for (int i = 0; i < mRestoreSets.length; i++) {
+                    if (token == mRestoreSets[i].token) {
+                        mWakelock.acquire();
+                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                        msg.obj = new RestoreParams(mRestoreTransport, observer, token);
+                        mBackupHandler.sendMessage(msg);
+                        return 0;
                     }
                 }
+            } else {
+                if (DEBUG) Log.v(TAG, "No current restore set, not doing restore");
+            }
+            return -1;
+        }
+
+        public void endRestoreSession() throws android.os.RemoteException {
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                    "endRestoreSession");
+
+            Log.d(TAG, "endRestoreSession");
+
+            mRestoreTransport.finishRestore();
+            mRestoreTransport = null;
+            synchronized(BackupManagerService.this) {
+                if (BackupManagerService.this.mActiveRestoreSession == this) {
+                    BackupManagerService.this.mActiveRestoreSession = null;
+                } else {
+                    Log.e(TAG, "ending non-current restore session");
+                }
             }
         }
     }
 
-    
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
+            long oldId = Binder.clearCallingIdentity();
+
+            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+                    + " / " + (!mProvisioned ? "not " : "") + "provisioned");
+            pw.println("Available transports:");
+            for (String t : listAllTransports()) {
+                String pad = (t.equals(mCurrentTransport)) ? "  * " : "    ";
+                pw.println(pad + t);
+            }
             int N = mBackupParticipants.size();
-            pw.println("Participants:");
+            pw.println("Participants: " + N);
             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());
+                HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+                for (ApplicationInfo app: participants) {
+                    pw.println("    " + app.toString());
                 }
             }
+            pw.println("Pending: " + mPendingBackups.size());
+            for (BackupRequest req : mPendingBackups.values()) {
+                pw.println("    " + req);
+            }
+
+            Binder.restoreCallingIdentity(oldId);
         }
     }
 }
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
new file mode 100644
index 0000000..28f09f5
--- /dev/null
+++ b/services/java/com/android/server/EntropyService.java
@@ -0,0 +1,148 @@
+/*
+ * 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 java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.util.Log;
+
+/**
+ * A service designed to load and periodically save &quot;randomness&quot;
+ * for the Linux kernel.
+ *
+ * <p>When a Linux system starts up, the entropy pool associated with
+ * {@code /dev/random} may be in a fairly predictable state.  Applications which
+ * depend strongly on randomness may find {@code /dev/random} or
+ * {@code /dev/urandom} returning predictable data.  In order to counteract
+ * this effect, it's helpful to carry the entropy pool information across
+ * shutdowns and startups.
+ *
+ * <p>This class was modeled after the script in
+ * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man
+ * 4 random</a>.
+ *
+ * <p>TODO: Investigate attempting to write entropy data at shutdown time
+ * instead of periodically.
+ */
+public class EntropyService extends Binder {
+    private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat";
+    private static final String TAG = "EntropyService";
+    private static final int ENTROPY_WHAT = 1;
+    private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000;  // 3 hrs
+    private static final String RANDOM_DEV = "/dev/urandom";
+    private static final long START_TIME = System.currentTimeMillis();
+    private static final long START_NANOTIME = System.nanoTime();
+
+    /**
+     * Handler that periodically updates the entropy on disk.
+     */
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what != ENTROPY_WHAT) {
+                Log.e(TAG, "Will not process invalid message");
+                return;
+            }
+            writeEntropy();
+            scheduleEntropyWriter();
+        }
+    };
+
+    public EntropyService() {
+        loadInitialEntropy();
+        addDeviceSpecificEntropy();
+        writeEntropy();
+        scheduleEntropyWriter();
+    }
+
+    private void scheduleEntropyWriter() {
+        mHandler.removeMessages(ENTROPY_WHAT);
+        mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD);
+    }
+
+    private void loadInitialEntropy() {
+        try {
+            RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV);
+        } catch (IOException e) {
+            Log.w(TAG, "unable to load initial entropy (first boot?)", e);
+        }
+    }
+
+    private void writeEntropy() {
+        try {
+            RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME);
+        } catch (IOException e) {
+            Log.w(TAG, "unable to write entropy", e);
+        }
+    }
+
+    /**
+     * Add additional information to the kernel entropy pool.  The
+     * information isn't necessarily "random", but that's ok.  Even
+     * sending non-random information to {@code /dev/urandom} is useful
+     * because, while it doesn't increase the "quality" of the entropy pool,
+     * it mixes more bits into the pool, which gives us a higher degree
+     * of uncertainty in the generated randomness.  Like nature, writes to
+     * the random device can only cause the quality of the entropy in the
+     * kernel to stay the same or increase.
+     *
+     * <p>For maximum effect, we try to target information which varies
+     * on a per-device basis, and is not easily observable to an
+     * attacker.
+     */
+    private void addDeviceSpecificEntropy() {
+        PrintWriter out = null;
+        try {
+            out = new PrintWriter(new FileOutputStream(RANDOM_DEV));
+            out.println("Copyright (C) 2009 The Android Open Source Project");
+            out.println("All Your Randomness Are Belong To Us");
+            out.println(START_TIME);
+            out.println(START_NANOTIME);
+            out.println(SystemProperties.get("ro.serialno"));
+            out.println(SystemProperties.get("ro.bootmode"));
+            out.println(SystemProperties.get("ro.baseband"));
+            out.println(SystemProperties.get("ro.carrier"));
+            out.println(SystemProperties.get("ro.bootloader"));
+            out.println(SystemProperties.get("ro.hardware"));
+            out.println(SystemProperties.get("ro.revision"));
+            out.println(System.currentTimeMillis());
+            out.println(System.nanoTime());
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to add device specific data to the entropy pool", e);
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    private static String getSystemDir() {
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        systemDir.mkdirs();
+        return systemDir.toString();
+    }
+}
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 3fc1e0e..9b0a2d4 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -35,6 +35,7 @@
  */
 class HeadsetObserver extends UEventObserver {
     private static final String TAG = HeadsetObserver.class.getSimpleName();
+    private static final boolean LOG = false;
 
     private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
     private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
@@ -61,7 +62,7 @@
 
     @Override
     public void onUEvent(UEventObserver.UEvent event) {
-        Log.v(TAG, "Headset UEVENT: " + event.toString());
+        if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString());
 
         try {
             update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 72efca5..d8c8c90 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -163,8 +163,24 @@
         return Collections.unmodifiableSet(mFilters);
     }
 
-    public List<R> queryIntent(ContentResolver resolver, Intent intent,
-            String resolvedType, boolean defaultOnly) {
+    public List<R> queryIntentFromList(Intent intent, String resolvedType, 
+            boolean defaultOnly, ArrayList<ArrayList<F>> listCut) {
+        ArrayList<R> resultList = new ArrayList<R>();
+
+        final boolean debug = localLOGV ||
+                ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+        final String scheme = intent.getScheme();
+        int N = listCut.size();
+        for (int i = 0; i < N; ++i) {
+            buildResolveList(intent, debug, defaultOnly,
+                             resolvedType, scheme, listCut.get(i), resultList);
+        }
+        sortResults(resultList);
+        return resultList;
+    }
+
+    public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
         String scheme = intent.getScheme();
 
         ArrayList<R> finalList = new ArrayList<R>();
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 05888e0..fab97b1 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -46,7 +46,6 @@
 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;
@@ -66,15 +65,12 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.util.Config;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
-import android.util.SparseIntArray;
 
 import com.android.internal.location.GpsLocationProvider;
 import com.android.internal.location.LocationProviderProxy;
 import com.android.internal.location.MockProvider;
-import com.android.server.am.BatteryStatsService;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -107,8 +103,6 @@
         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>();
@@ -171,9 +165,6 @@
     private HashMap<String,Location> mLastKnownLocation =
         new HashMap<String,Location>();
 
-    // Location collector
-    private ILocationCollector mCollector;
-
     private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
 
     // for Settings change notification
@@ -516,6 +507,7 @@
 
     private void removeProvider(LocationProviderProxy provider) {
         mProviders.remove(provider);
+        provider.unlinkProvider();
         mProvidersByName.remove(provider.getName());
     }
 
@@ -630,16 +622,6 @@
         }
     }
 
-    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;
-    }
-
     public void installGeocodeProvider(IGeocodeProvider provider) {
         if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -666,14 +648,14 @@
 
     private void checkPermissionsSafe(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider)
-            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
+            && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
                 != PackageManager.PERMISSION_GRANTED)) {
             throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
         }
         if (LocationManager.NETWORK_PROVIDER.equals(provider)
-            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
+            && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
                 != PackageManager.PERMISSION_GRANTED)
-            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
+            && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
                 != PackageManager.PERMISSION_GRANTED)) {
             throw new SecurityException(
                 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
@@ -682,14 +664,14 @@
 
     private boolean isAllowedProviderSafe(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider)
-            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
+            && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
                 != PackageManager.PERMISSION_GRANTED)) {
             return false;
         }
         if (LocationManager.NETWORK_PROVIDER.equals(provider)
-            && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
+            && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
                 != PackageManager.PERMISSION_GRANTED)
-            && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
+            && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
                 != PackageManager.PERMISSION_GRANTED)) {
             return false;
         }
@@ -788,8 +770,8 @@
                 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
                     if (deadReceivers == null) {
                         deadReceivers = new ArrayList<Receiver>();
-                        deadReceivers.add(record.mReceiver);
                     }
+                    deadReceivers.add(record.mReceiver);
                 }
                 listeners++;
             }
@@ -1093,7 +1075,7 @@
         if (mGpsStatusProvider == null) {
             return false;
         }
-        if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
+        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
                 PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
         }
@@ -1121,7 +1103,7 @@
         // first check for permission to the provider
         checkPermissionsSafe(provider);
         // and check for ACCESS_LOCATION_EXTRA_COMMANDS
-        if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
+        if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
                 != PackageManager.PERMISSION_GRANTED)) {
             throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
         }
@@ -1619,23 +1601,19 @@
 
                     synchronized (mLock) {
                         Location location = (Location) msg.obj;
+                        String provider = location.getProvider();
 
-                        if (mCollector != null && 
-                                LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
-                            try {
-                                mCollector.updateLocation(location);
-                            } catch (RemoteException e) {
-                                Log.w(TAG, "mCollector.updateLocation failed");
-                                mCollector = null;
+                        // notify other providers of the new location
+                        for (int i = mProviders.size() - 1; i >= 0; i--) {
+                            LocationProviderProxy proxy = mProviders.get(i);
+                            if (!provider.equals(proxy.getName())) {
+                                proxy.updateLocation(location);
                             }
                         }
 
-                        String provider = location.getProvider();
-                        if (!isAllowedBySettingsLocked(provider)) {
-                            return;
+                        if (isAllowedBySettingsLocked(provider)) {
+                            handleLocationChangedLocked(location);
                         }
-
-                        handleLocationChangedLocked(location);
                     }
                 }
             } catch (Exception e) {
@@ -1935,7 +1913,6 @@
         synchronized (mLock) {
             pw.println("Current Location Manager state:");
             pw.println("  sProvidersLoaded=" + sProvidersLoaded);
-            pw.println("  mCollector=" + mCollector);
             pw.println("  Listeners:");
             int N = mReceivers.size();
             for (int i=0; i<N; i++) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6ed8b4c..854138c 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,47 +16,52 @@
 
 package com.android.server;
 
+import com.android.server.status.IconData;
+import com.android.server.status.NotificationData;
+import com.android.server.status.StatusBarService;
+
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+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.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
-import android.media.AudioManager;
+import android.database.ContentObserver;
 import android.media.AsyncPlayer;
-import android.media.RingtoneManager;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Binder;
-import android.os.RemoteException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Power;
+import android.os.RemoteException;
 import android.os.Vibrator;
 import android.provider.Settings;
-import android.util.Config;
+import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
 
-import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
-import com.android.server.status.StatusBarService;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.io.IOException;
 
 class NotificationManagerService extends INotificationManager.Stub
 {
@@ -88,6 +93,12 @@
     private NotificationRecord mVibrateNotification;
     private Vibrator mVibrator = new Vibrator();
 
+    // adb
+    private int mBatteryPlugged;
+    private boolean mAdbEnabled = false;
+    private boolean mAdbNotificationShown = false;
+    private Notification mAdbNotification;
+    
     private ArrayList<NotificationRecord> mNotificationList;
 
     private ArrayList<ToastRecord> mToastQueue;
@@ -98,7 +109,7 @@
     private boolean mBatteryLow;
     private boolean mBatteryFull;
     private NotificationRecord mLedNotification;
-    
+
     private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
     private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00;    // Charging - orange solid on
     private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
@@ -297,6 +308,9 @@
                     mBatteryFull = batteryFull;
                     updateLights();
                 }
+                
+                mBatteryPlugged = intent.getIntExtra("plugged", 0);
+                updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
                 Uri uri = intent.getData();
@@ -312,6 +326,31 @@
         }
     };
 
+    class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+        
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.ADB_ENABLED), false, this);
+            update();
+        }
+
+        @Override public void onChange(boolean selfChange) {
+            update();
+        }
+
+        public void update() {
+            ContentResolver resolver = mContext.getContentResolver();
+            mAdbEnabled = Settings.Secure.getInt(resolver,
+                        Settings.Secure.ADB_ENABLED, 0) != 0;
+            updateAdbNotification();
+        }
+    }
+    private final SettingsObserver mSettingsObserver;
+    
     NotificationManagerService(Context context, StatusBarService statusBar,
             HardwareService hardware)
     {
@@ -333,6 +372,9 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         mContext.registerReceiver(mIntentReceiver, filter);
+        
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mSettingsObserver.observe();
     }
 
     // Toasts
@@ -594,6 +636,9 @@
                         Binder.restoreCallingIdentity(identity);
                     }
                 }
+
+                sendAccessibilityEventTypeNotificationChangedDoCheck(notification, pkg);
+
             } else {
                 if (old != null && old.statusBarKey != null) {
                     long identity = Binder.clearCallingIdentity();
@@ -676,6 +721,26 @@
         idOut[0] = id;
     }
 
+    private void sendAccessibilityEventTypeNotificationChangedDoCheck(Notification notification,
+            CharSequence packageName) {
+        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+        if (!manager.isEnabled()) {
+            return;
+        }
+
+        AccessibilityEvent event =
+            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+        event.setPackageName(packageName);
+        event.setClassName(Notification.class.getName());
+        event.setParcelableData(notification);
+        CharSequence tickerText = notification.tickerText;
+        if (!TextUtils.isEmpty(tickerText)) {
+            event.getText().add(tickerText);
+        }
+
+        manager.sendAccessibilityEvent(event);
+    }
+
     private void cancelNotificationLocked(NotificationRecord r) {
         // status bar
         if (r.notification.icon != 0) {
@@ -869,6 +934,62 @@
         return -1;
     }
 
+    // This is here instead of StatusBarPolicy because it is an important
+    // security feature that we don't want people customizing the platform
+    // to accidentally lose.
+    private void updateAdbNotification() {
+        if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+            if (!mAdbNotificationShown) {
+                NotificationManager notificationManager = (NotificationManager) mContext
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
+                if (notificationManager != null) {
+                    Resources r = mContext.getResources();
+                    CharSequence title = r.getText(
+                            com.android.internal.R.string.adb_active_notification_title);
+                    CharSequence message = r.getText(
+                            com.android.internal.R.string.adb_active_notification_message);
+
+                    if (mAdbNotification == null) {
+                        mAdbNotification = new Notification();
+                        mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
+                        mAdbNotification.when = 0;
+                        mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
+                        mAdbNotification.tickerText = title;
+                        mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
+                    }
+
+                    Intent intent = new Intent(
+                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    // Note: we are hard-coding the component because this is
+                    // an important security UI that we don't want anyone
+                    // intercepting.
+                    intent.setComponent(new ComponentName("com.android.settings",
+                            "com.android.settings.DevelopmentSettings"));
+                    PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+                            intent, 0);
+
+                    mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
+                    
+                    mAdbNotificationShown = true;
+                    notificationManager.notify(
+                            com.android.internal.R.string.adb_active_notification_title,
+                            mAdbNotification);
+                }
+            }
+            
+        } else if (mAdbNotificationShown) {
+            NotificationManager notificationManager = (NotificationManager) mContext
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            if (notificationManager != null) {
+                mAdbNotificationShown = false;
+                notificationManager.cancel(
+                        com.android.internal.R.string.adb_active_notification_title);
+            }
+        }
+    }
+    
     // ======================================================================
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
new file mode 100644
index 0000000..786f423
--- /dev/null
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -0,0 +1,413 @@
+/*
+ * 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.app.BackupAgent;
+import android.backup.BackupDataInput;
+import android.backup.BackupDataOutput;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * We back up the signatures of each package so that during a system restore,
+ * we can verify that the app whose data we think we have matches the app
+ * actually resident on the device.
+ *
+ * Since the Package Manager isn't a proper "application" we just provide a
+ * direct IBackupAgent implementation and hand-construct it at need.
+ */
+public class PackageManagerBackupAgent extends BackupAgent {
+    private static final String TAG = "PMBA";
+    private static final boolean DEBUG = true;
+
+    // key under which we store global metadata (individual app metadata
+    // is stored using the package name as a key)
+    private static final String GLOBAL_METADATA_KEY = "@meta@";
+
+    private List<PackageInfo> mAllPackages;
+    private PackageManager mPackageManager;
+    // version & signature info of each app in a restore set
+    private HashMap<String, Metadata> mRestoredSignatures;
+    // The version info of each backed-up app as read from the state file
+    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
+
+    private final HashSet<String> mExisting = new HashSet<String>();
+    private int mStoredSdkVersion;
+    private String mStoredIncrementalVersion;
+    private boolean mHasMetadata;
+
+    public class Metadata {
+        public int versionCode;
+        public Signature[] signatures;
+
+        Metadata(int version, Signature[] sigs) {
+            versionCode = version;
+            signatures = sigs;
+        }
+    }
+
+    // We're constructed with the set of applications that are participating
+    // in backup.  This set changes as apps are installed & removed.
+    PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
+        mPackageManager = packageMgr;
+        mAllPackages = packages;
+        mRestoredSignatures = null;
+        mHasMetadata = false;
+    }
+
+    public boolean hasMetadata() {
+        return mHasMetadata;
+    }
+
+    public Metadata getRestoredMetadata(String packageName) {
+        if (mRestoredSignatures == null) {
+            Log.w(TAG, "getRestoredMetadata() before metadata read!");
+            return null;
+        }
+
+        return mRestoredSignatures.get(packageName);
+    }
+    
+    // The backed up data is the signature block for each app, keyed by
+    // the package name.
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) {
+        if (DEBUG) Log.v(TAG, "onBackup()");
+
+        ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these
+        DataOutputStream outWriter = new DataOutputStream(bufStream);
+        parseStateFile(oldState);
+
+        // If the stored version string differs, we need to re-backup all
+        // of the metadata.  We force this by removing everything from the
+        // "already backed up" map built by parseStateFile().
+        if (mStoredIncrementalVersion == null
+                || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
+            Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
+                    + Build.VERSION.INCREMENTAL + " - rewriting");
+            mExisting.clear();
+        }
+
+        try {
+            /*
+             * Global metadata:
+             *
+             * int SDKversion -- the SDK version of the OS itself on the device
+             *                   that produced this backup set.  Used to reject
+             *                   backups from later OSes onto earlier ones.
+             * String incremental -- the incremental release name of the OS stored in
+             *                       the backup set.
+             */
+            if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
+                if (DEBUG) Log.v(TAG, "Storing global metadata key");
+                outWriter.writeInt(Build.VERSION.SDK_INT);
+                outWriter.writeUTF(Build.VERSION.INCREMENTAL);
+                byte[] metadata = bufStream.toByteArray();
+                data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
+                data.writeEntityData(metadata, metadata.length);
+            } else {
+                if (DEBUG) Log.v(TAG, "Global metadata key already stored");
+                // don't consider it to have been skipped/deleted
+                mExisting.remove(GLOBAL_METADATA_KEY);
+            }
+
+            // For each app we have on device, see if we've backed it up yet.  If not,
+            // write its signature block to the output, keyed on the package name.
+            for (PackageInfo pkg : mAllPackages) {
+                String packName = pkg.packageName;
+                if (packName.equals(GLOBAL_METADATA_KEY)) {
+                    // We've already handled the metadata key; skip it here
+                    continue;
+                } else {
+                    PackageInfo info = null;
+                    try {
+                        info = mPackageManager.getPackageInfo(packName,
+                                PackageManager.GET_SIGNATURES);
+                    } catch (NameNotFoundException e) {
+                        // Weird; we just found it, and now are told it doesn't exist.
+                        // Treat it as having been removed from the device.
+                        mExisting.add(packName);
+                        continue;
+                    }
+
+                    boolean doBackup = false;
+                    if (!mExisting.contains(packName)) {
+                        // We haven't backed up this app before
+                        doBackup = true;
+                    } else {
+                        // We *have* backed this one up before.  Check whether the version
+                        // of the backup matches the version of the current app; if they
+                        // don't match, the app has been updated and we need to store its
+                        // metadata again.  In either case, take it out of mExisting so that
+                        // we don't consider it deleted later.
+                        if (info.versionCode != mStateVersions.get(packName).versionCode) {
+                            doBackup = true;
+                        }
+                        mExisting.remove(packName);
+                    }
+
+                    if (doBackup) {
+                        // We need to store this app's metadata
+                        /*
+                         * Metadata for each package:
+                         *
+                         * int version       -- [4] the package's versionCode
+                         * byte[] signatures -- [len] flattened Signature[] of the package
+                         */
+
+                        // marshal the version code in a canonical form
+                        bufStream.reset();
+                        outWriter.writeInt(info.versionCode);
+                        byte[] versionBuf = bufStream.toByteArray();
+
+                        byte[] sigs = flattenSignatureArray(info.signatures);
+
+                        // !!! TODO: take out this debugging
+                        if (DEBUG) {
+                            Log.v(TAG, "+ metadata for " + packName
+                                    + " version=" + info.versionCode
+                                    + " versionLen=" + versionBuf.length
+                                    + " sigsLen=" + sigs.length);
+                        }
+                        // Now we can write the backup entity for this package
+                        data.writeEntityHeader(packName, versionBuf.length + sigs.length);
+                        data.writeEntityData(versionBuf, versionBuf.length);
+                        data.writeEntityData(sigs, sigs.length);
+                    }
+                }
+            }
+
+            // At this point, the only entries in 'existing' are apps that were
+            // mentioned in the saved state file, but appear to no longer be present
+            // on the device.  Write a deletion entity for them.
+            for (String app : mExisting) {
+                // !!! TODO: take out this msg
+                if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
+                try {
+                    data.writeEntityHeader(app, -1);
+                } catch (IOException e) {
+                    Log.e(TAG, "Unable to write package deletions!");
+                    return;
+                }
+            }
+        } catch (IOException e) {
+            // Real error writing data
+            Log.e(TAG, "Unable to write package backup data file!");
+            return;
+        }
+
+        // Finally, write the new state blob -- just the list of all apps we handled
+        writeStateFile(mAllPackages, newState);
+    }
+
+    // "Restore" here is a misnomer.  What we're really doing is reading back the
+    // set of app signatures associated with each backed-up app in this restore
+    // image.  We'll use those later to determine what we can legitimately restore.
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+        List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
+        HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
+        if (DEBUG) Log.v(TAG, "onRestore()");
+        int storedSystemVersion = -1;
+
+        while (data.readNextHeader()) {
+            String key = data.getKey();
+            int dataSize = data.getDataSize();
+
+            if (DEBUG) Log.v(TAG, "   got key=" + key + " dataSize=" + dataSize);
+
+            // generic setup to parse any entity data
+            byte[] dataBuf = new byte[dataSize];
+            data.readEntityData(dataBuf, 0, dataSize);
+            ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
+            DataInputStream in = new DataInputStream(baStream);
+
+            if (key.equals(GLOBAL_METADATA_KEY)) {
+                int storedSdkVersion = in.readInt();
+                if (DEBUG) Log.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
+                if (storedSystemVersion > Build.VERSION.SDK_INT) {
+                    // returning before setting the sig map means we rejected the restore set
+                    Log.w(TAG, "Restore set was from a later version of Android; not restoring");
+                    return;
+                }
+                mStoredSdkVersion = storedSdkVersion;
+                mStoredIncrementalVersion = in.readUTF();
+                mHasMetadata = true;
+                // !!! TODO: remove this debugging output
+                if (DEBUG) {
+                    Log.i(TAG, "Restore set version " + storedSystemVersion
+                            + " is compatible with OS version " + Build.VERSION.SDK_INT
+                            + " (" + mStoredIncrementalVersion + " vs "
+                            + Build.VERSION.INCREMENTAL + ")");
+                }
+            } else {
+                // it's a file metadata record
+                int versionCode = in.readInt();
+                Signature[] sigs = unflattenSignatureArray(in);
+//              !!! TODO: take out this debugging
+                if (DEBUG) {
+                    Log.i(TAG, "   restored metadata for " + key
+                            + " dataSize=" + dataSize
+                            + " versionCode=" + versionCode + " sigs=" + sigs);
+                }
+
+                ApplicationInfo app = new ApplicationInfo();
+                app.packageName = key;
+                restoredApps.add(app);
+                sigMap.put(key, new Metadata(versionCode, sigs));
+            }
+        }
+
+        // On successful completion, cache the signature map for the Backup Manager to use
+        mRestoredSignatures = sigMap;
+    }
+
+
+    // Util: convert an array of Signatures into a flattened byte buffer.  The
+    // flattened format contains enough info to reconstruct the signature array.
+    private byte[] flattenSignatureArray(Signature[] allSigs) {
+        ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(outBuf);
+
+        // build the set of subsidiary buffers
+        try {
+            // first the # of signatures in the array
+            out.writeInt(allSigs.length);
+
+            // then the signatures themselves, length + flattened buffer
+            for (Signature sig : allSigs) {
+                byte[] flat = sig.toByteArray();
+                out.writeInt(flat.length);
+                out.write(flat);
+            }
+        } catch (IOException e) {
+            // very strange; we're writing to memory here.  abort.
+            return null;
+        }
+
+        return outBuf.toByteArray();
+    }
+
+    private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
+        Signature[] sigs = null;
+
+        try {
+            int num = in.readInt();
+            Log.v(TAG, " ... unflatten read " + num);
+            sigs = new Signature[num];
+            for (int i = 0; i < num; i++) {
+                int len = in.readInt();
+                byte[] flatSig = new byte[len];
+                in.read(flatSig);
+                sigs[i] = new Signature(flatSig);
+            }
+        } catch (EOFException e) {
+            // clean termination
+            if (sigs == null) {
+                Log.w(TAG, "Empty signature block found");
+            }
+        } catch (IOException e) {
+            Log.d(TAG, "Unable to unflatten sigs");
+            return null;
+        }
+
+        return sigs;
+    }
+
+    // Util: parse out an existing state file into a usable structure
+    private void parseStateFile(ParcelFileDescriptor stateFile) {
+        mExisting.clear();
+        mStateVersions.clear();
+        mStoredSdkVersion = 0;
+        mStoredIncrementalVersion = null;
+
+        // The state file is just the list of app names we have stored signatures for
+        // with the exception of the metadata block, to which is also appended the
+        // version numbers corresponding with the last time we wrote this PM block.
+        // If they mismatch the current system, we'll re-store the metadata key.
+        FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
+        DataInputStream in = new DataInputStream(instream);
+
+        int bufSize = 256;
+        byte[] buf = new byte[bufSize];
+        try {
+            String pkg = in.readUTF();
+            if (pkg.equals(GLOBAL_METADATA_KEY)) {
+                mStoredSdkVersion = in.readInt();
+                mStoredIncrementalVersion = in.readUTF();
+                mExisting.add(GLOBAL_METADATA_KEY);
+            } else {
+                Log.e(TAG, "No global metadata in state file!");
+                return;
+            }
+
+            // The global metadata was first; now read all the apps
+            while (true) {
+                pkg = in.readUTF();
+                int versionCode = in.readInt();
+                mExisting.add(pkg);
+                mStateVersions.put(pkg, new Metadata(versionCode, null));
+            }
+        } catch (EOFException eof) {
+            // safe; we're done
+        } catch (IOException e) {
+            // whoops, bad state file.  abort.
+            Log.e(TAG, "Unable to read Package Manager state file: " + e);
+        }
+    }
+
+    // Util: write out our new backup state file
+    private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
+        FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
+        DataOutputStream out = new DataOutputStream(outstream);
+
+        try {
+            // by the time we get here we know we've stored the global metadata record
+            out.writeUTF(GLOBAL_METADATA_KEY);
+            out.writeInt(Build.VERSION.SDK_INT);
+            out.writeUTF(Build.VERSION.INCREMENTAL);
+
+            // now write all the app names too
+            for (PackageInfo pkg : pkgs) {
+                out.writeUTF(pkg.packageName);
+                out.writeInt(pkg.versionCode);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write package manager state file!");
+            return;
+        }
+    }
+}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 079f363..06435c8 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -26,13 +26,12 @@
 
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
@@ -57,6 +56,8 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -147,6 +148,7 @@
     
     final Context mContext;
     final boolean mFactoryTest;
+    final boolean mNoDexOpt;
     final DisplayMetrics mMetrics;
     final int mDefParseFlags;
     final String[] mSeparateProcesses;
@@ -258,6 +260,7 @@
     final ResolveInfo mResolveInfo = new ResolveInfo();
     ComponentName mResolveComponentName;
     PackageParser.Package mPlatformPackage;
+    private boolean mCompatibilityModeEnabled = true;
 
     public static final IPackageManager main(Context context, boolean factoryTest) {
         PackageManagerService m = new PackageManagerService(context, factoryTest);
@@ -297,6 +300,7 @@
         
         mContext = context;
         mFactoryTest = factoryTest;
+        mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
         mMetrics = new DisplayMetrics();
         mSettings = new Settings();
         mSettings.addSharedUserLP("android.uid.system",
@@ -366,6 +370,10 @@
                     startTime);
             
             int scanMode = SCAN_MONITOR;
+            if (mNoDexOpt) {
+                Log.w(TAG, "Running ENG build: no pre-dexopt!");
+                scanMode |= SCAN_NO_DEX; 
+            }
             
             final HashSet<String> libFiles = new HashSet<String>();
             
@@ -508,7 +516,7 @@
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
     }
-    
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -884,7 +892,11 @@
                     + ": " + p);
             if (p != null) {
                 // Note: isEnabledLP() does not apply here - always return info
-                return PackageParser.generateApplicationInfo(p, flags);
+                ApplicationInfo appInfo = PackageParser.generateApplicationInfo(p, flags);
+                if (!mCompatibilityModeEnabled) {
+                    appInfo.disableCompatibilityMode();
+                }
+                return appInfo;
             }
             if ("android".equals(packageName)||"system".equals(packageName)) {
                 return mAndroidApplication;
@@ -922,7 +934,7 @@
         });
     }
 
-    public void freeStorage(final long freeStorageSize, final PendingIntent opFinishedIntent) {
+    public void freeStorage(final long freeStorageSize, final IntentSender pi) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CLEAR_APP_CACHE, null);
         // Queue up an async operation since clearing cache may take a little while.
@@ -936,11 +948,13 @@
                         Log.w(TAG, "Couldn't clear application caches");
                     }
                 }
-                if(opFinishedIntent != null) {
+                if(pi != null) {
                     try {
                         // Callback via pending intent
-                        opFinishedIntent.send((retCode >= 0) ? 1 : 0);
-                    } catch (CanceledException e1) {
+                        int code = (retCode >= 0) ? 1 : 0;
+                        pi.sendIntent(null, code, null,
+                                null, null);
+                    } catch (SendIntentException e1) {
                         Log.i(TAG, "Failed to send pending intent");
                     }
                 }
@@ -951,8 +965,8 @@
     public ActivityInfo getActivityInfo(ComponentName component, int flags) {
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
-            if (Config.LOGV) Log.v(
-                TAG, "getActivityInfo " + component + ": " + a);
+
+            if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledLP(a.info, flags)) {
                 return PackageParser.generateActivityInfo(a, flags);
             }
@@ -1203,6 +1217,11 @@
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags) {
         List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags);
+        return chooseBestActivity(intent, resolvedType, flags, query);
+    }
+
+    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
+                                           int flags, List<ResolveInfo> query) {
         if (query != null) {
             final int N = query.size();
             if (N == 1) {
@@ -1243,8 +1262,7 @@
         synchronized (mPackages) {
             if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
             List<PreferredActivity> prefs =
-                    mSettings.mPreferredActivities.queryIntent(null,
-                            intent, resolvedType,
+                    mSettings.mPreferredActivities.queryIntent(intent, resolvedType,
                             (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
             if (prefs != null && prefs.size() > 0) {
                 // First figure out how good the original match set is.
@@ -1320,8 +1338,17 @@
         }
 
         synchronized (mPackages) {
-            return (List<ResolveInfo>)mActivities.
-                queryIntent(null, intent, resolvedType, flags);
+            String pkgName = intent.getPackage();
+            if (pkgName == null) {
+                return (List<ResolveInfo>)mActivities.queryIntent(intent,
+                        resolvedType, flags);
+            }
+            PackageParser.Package pkg = mPackages.get(pkgName);
+            if (pkg != null) {
+                return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,
+                        resolvedType, flags, pkg.activities);
+            }
+            return null;
         }
     }
 
@@ -1488,9 +1515,30 @@
 
     public List<ResolveInfo> queryIntentReceivers(Intent intent,
             String resolvedType, int flags) {
+        ComponentName comp = intent.getComponent();
+        if (comp != null) {
+            List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            ActivityInfo ai = getReceiverInfo(comp, flags);
+            if (ai != null) {
+                ResolveInfo ri = new ResolveInfo();
+                ri.activityInfo = ai;
+                list.add(ri);
+            }
+            return list;
+        }
+        
         synchronized (mPackages) {
-            return (List<ResolveInfo>)mReceivers.
-                queryIntent(null, intent, resolvedType, flags);
+            String pkgName = intent.getPackage();
+            if (pkgName == null) {
+                return (List<ResolveInfo>)mReceivers.queryIntent(intent,
+                        resolvedType, flags);
+            }
+            PackageParser.Package pkg = mPackages.get(pkgName);
+            if (pkg != null) {
+                return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent,
+                        resolvedType, flags, pkg.receivers);
+            }
+            return null;
         }
     }
 
@@ -1523,8 +1571,17 @@
         }
 
         synchronized (mPackages) {
-            return (List<ResolveInfo>)mServices.
-                queryIntent(null, intent, resolvedType, flags);
+            String pkgName = intent.getPackage();
+            if (pkgName == null) {
+                return (List<ResolveInfo>)mServices.queryIntent(intent,
+                        resolvedType, flags);
+            }
+            PackageParser.Package pkg = mPackages.get(pkgName);
+            if (pkg != null) {
+                return (List<ResolveInfo>)mServices.queryIntentForPackage(intent,
+                        resolvedType, flags, pkg.services);
+            }
+            return null;
         }
     }
     
@@ -1837,7 +1894,56 @@
         }
         return true;
     }
+    
+    public boolean performDexOpt(String packageName) {
+        if (!mNoDexOpt) {
+            return false;
+        }
         
+        PackageParser.Package p;
+        synchronized (mPackages) {
+            p = mPackages.get(packageName);
+            if (p == null || p.mDidDexOpt) {
+                return false;
+            }
+        }
+        synchronized (mInstallLock) {
+            return performDexOptLI(p, false) == DEX_OPT_PERFORMED;
+        }
+    }
+    
+    static final int DEX_OPT_SKIPPED = 0;
+    static final int DEX_OPT_PERFORMED = 1;
+    static final int DEX_OPT_FAILED = -1;
+    
+    private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) {
+        boolean performed = false;
+        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0 && mInstaller != null) {
+            String path = pkg.mScanPath;
+            int ret = 0;
+            try {
+                if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
+                    ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, 
+                            !pkg.mForwardLocked);
+                    pkg.mDidDexOpt = true;
+                    performed = true;
+                }
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Apk not found for dexopt: " + path);
+                ret = -1;
+            } catch (IOException e) {
+                Log.w(TAG, "Exception reading apk: " + path, e);
+                ret = -1;
+            }
+            if (ret < 0) {
+                //error from installer
+                return DEX_OPT_FAILED;
+            }
+        }
+        
+        return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+    }
+    
     private PackageParser.Package scanPackageLI(
         File scanFile, File destCodeFile, File destResourceFile,
         PackageParser.Package pkg, int parseFlags, int scanMode) {
@@ -2126,37 +2232,18 @@
             String path = scanFile.getPath();
             if (scanFileNewer) {
                 Log.i(TAG, path + " changed; unpacking");
-                try {
-                    cachePackageSharedLibsLI(pkg, dataPath, scanFile);
-                } catch (IOException e) {
-                    Log.e(TAG, "Failure extracting shared libs", e);
-                    if(mInstaller != null) {
-                        mInstaller.remove(pkgName);
-                    } else {
-                        dataPath.delete();
-                    }
-                    mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                int err = cachePackageSharedLibsLI(pkg, dataPath, scanFile);
+                if (err != PackageManager.INSTALL_SUCCEEDED) {
+                    mLastScanError = err;
                     return null;
                 }
             }
 
-            if ((scanMode&SCAN_NO_DEX) == 0
-                    && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
-                int ret = 0;
-                try {
-                    if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
-                        ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, 
-                                (scanMode&SCAN_FORWARD_LOCKED) == 0);
-                    }
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "Apk not found for dexopt: " + path);
-                    ret = -1;
-                } catch (IOException e) {
-                    Log.w(TAG, "Exception reading apk: " + path, e);
-                    ret = -1;
-                }
-                if (ret < 0) {
-                    //error from installer
+            pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0;
+            pkg.mScanPath = path;
+            
+            if ((scanMode&SCAN_NO_DEX) == 0) {
+                if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) {
                     mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
                     return null;
                 }
@@ -2419,14 +2506,15 @@
         return pkg;
     }
 
-    private void cachePackageSharedLibsLI(PackageParser.Package pkg,
-            File dataPath, File scanFile) throws IOException {
+    private int cachePackageSharedLibsLI(PackageParser.Package pkg,
+            File dataPath, File scanFile) {
         File sharedLibraryDir = new File(dataPath.getPath() + "/lib");
-        final String sharedLibraryABI = "armeabi";
+        final String sharedLibraryABI = Build.CPU_ABI;
         final String apkLibraryDirectory = "lib/" + sharedLibraryABI + "/";
         final String apkSharedLibraryPrefix = apkLibraryDirectory + "lib";
         final String sharedLibrarySuffix = ".so";
-        boolean createdSharedLib = false;
+        boolean hasNativeCode = false;
+        boolean installedNativeCode = false;
         try {
             ZipFile zipFile = new ZipFile(scanFile);
             Enumeration<ZipEntry> entries =
@@ -2435,9 +2523,15 @@
             while (entries.hasMoreElements()) {
                 ZipEntry entry = entries.nextElement();
                 if (entry.isDirectory()) {
+                    if (!hasNativeCode && entry.getName().startsWith("lib")) {
+                        hasNativeCode = true;
+                    }
                     continue;
                 }
                 String entryName = entry.getName();
+                if (entryName.startsWith("lib/")) {
+                    hasNativeCode = true;
+                }
                 if (! (entryName.startsWith(apkSharedLibraryPrefix)
                         && entryName.endsWith(sharedLibrarySuffix))) {
                     continue;
@@ -2448,6 +2542,9 @@
                         || (!FileUtils.isFilenameSafe(new File(libFileName)))) {
                     continue;
                 }
+                
+                installedNativeCode = true;
+                
                 String sharedLibraryFilePath = sharedLibraryDir.getPath() +
                     File.separator + libFileName;
                 File sharedLibraryFile = new File(sharedLibraryFilePath);
@@ -2459,19 +2556,23 @@
                     }
                     if (mInstaller == null) {
                         sharedLibraryDir.mkdir();
-                        createdSharedLib = true;
                     }
                     cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir,
                             sharedLibraryFile);
                 }
             }
         } catch (IOException e) {
-            Log.e(TAG, "Failed to cache package shared libs", e);
-            if(createdSharedLib) {
-                sharedLibraryDir.delete();
-            }
-            throw e;
+            Log.w(TAG, "Failed to cache package shared libs", e);
+            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
         }
+        
+        if (hasNativeCode && !installedNativeCode) {
+            Log.w(TAG, "Install failed: .apk has native code but none for arch "
+                    + Build.CPU_ABI);
+            return PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE;
+        }
+        
+        return PackageManager.INSTALL_SUCCEEDED;
     }
 
     private void cacheSharedLibLI(PackageParser.Package pkg,
@@ -2801,6 +2902,21 @@
                         // we can't add any new permissions to it.
                         if (!gp.loadedPermissions.contains(perm)) {
                             allowed = false;
+                            // Except...  if this is a permission that was added
+                            // to the platform (note: need to only do this when
+                            // updating the platform).
+                            final int NP = PackageParser.NEW_PERMISSIONS.length;
+                            for (int ip=0; ip<NP; ip++) {
+                                final PackageParser.NewPermissionInfo npi
+                                        = PackageParser.NEW_PERMISSIONS[ip];
+                                if (npi.name.equals(perm)
+                                        && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
+                                    allowed = true;
+                                    Log.i(TAG, "Auto-granting WRITE_EXTERNAL_STORAGE to old pkg "
+                                            + pkg.packageName);
+                                    break;
+                                }
+                            }
                         }
                     }
                     if (allowed) {
@@ -2839,20 +2955,38 @@
 
     private final class ActivityIntentResolver
             extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
-        public List queryIntent(ContentResolver resolver, Intent intent,
-                String resolvedType, boolean defaultOnly) {
+        public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
             mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
-            return super.queryIntent(resolver, intent, resolvedType, defaultOnly);
+            return super.queryIntent(intent, resolvedType, defaultOnly);
         }
 
-        public List queryIntent(ContentResolver resolver, Intent intent,
-                String resolvedType, int flags) {
+        public List queryIntent(Intent intent, String resolvedType, int flags) {
             mFlags = flags;
-            return super.queryIntent(
-                resolver, intent, resolvedType,
+            return super.queryIntent(intent, resolvedType,
                 (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
         }
 
+        public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
+                                          ArrayList<PackageParser.Activity> packageActivities) {
+            if (packageActivities == null) {
+                return null;
+            }
+            mFlags = flags;
+            final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
+            int N = packageActivities.size();
+            ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut =
+                new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N);
+
+            ArrayList<PackageParser.ActivityIntentInfo> intentFilters;
+            for (int i = 0; i < N; ++i) {
+                intentFilters = packageActivities.get(i).intents;
+                if (intentFilters != null && intentFilters.size() > 0) {
+                    listCut.add(intentFilters);
+                }
+            }
+            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut);
+        }
+
         public final void addActivity(PackageParser.Activity a, String type) {
             mActivities.put(a.component, a);
             if (SHOW_INFO || Config.LOGV) Log.v(
@@ -2860,8 +2994,7 @@
                 (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":");
             if (SHOW_INFO || Config.LOGV) Log.v(TAG, "    Class=" + a.info.name);
             int NI = a.intents.size();
-            int j;
-            for (j=0; j<NI; j++) {
+            for (int j=0; j<NI; j++) {
                 PackageParser.ActivityIntentInfo intent = a.intents.get(j);
                 if (SHOW_INFO || Config.LOGV) {
                     Log.v(TAG, "    IntentFilter:");
@@ -2881,8 +3014,7 @@
                 (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":");
             if (SHOW_INFO || Config.LOGV) Log.v(TAG, "    Class=" + a.info.name);
             int NI = a.intents.size();
-            int j;
-            for (j=0; j<NI; j++) {
+            for (int j=0; j<NI; j++) {
                 PackageParser.ActivityIntentInfo intent = a.intents.get(j);
                 if (SHOW_INFO || Config.LOGV) {
                     Log.v(TAG, "    IntentFilter:");
@@ -2969,20 +3101,38 @@
 
     private final class ServiceIntentResolver
             extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> {
-        public List queryIntent(ContentResolver resolver, Intent intent,
-                String resolvedType, boolean defaultOnly) {
+        public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
             mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
-            return super.queryIntent(resolver, intent, resolvedType, defaultOnly);
+            return super.queryIntent(intent, resolvedType, defaultOnly);
         }
 
-        public List queryIntent(ContentResolver resolver, Intent intent,
-                String resolvedType, int flags) {
+        public List queryIntent(Intent intent, String resolvedType, int flags) {
             mFlags = flags;
-            return super.queryIntent(
-                resolver, intent, resolvedType,
+            return super.queryIntent(intent, resolvedType,
                 (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
         }
 
+        public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
+                                          ArrayList<PackageParser.Service> packageServices) {
+            if (packageServices == null) {
+                return null;
+            }
+            mFlags = flags;
+            final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
+            int N = packageServices.size();
+            ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut =
+                new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N);
+
+            ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
+            for (int i = 0; i < N; ++i) {
+                intentFilters = packageServices.get(i).intents;
+                if (intentFilters != null && intentFilters.size() > 0) {
+                    listCut.add(intentFilters);
+                }
+            }
+            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut);
+        }
+
         public final void addService(PackageParser.Service s) {
             mServices.put(s.component, s);
             if (SHOW_INFO || Config.LOGV) Log.v(
@@ -3142,6 +3292,7 @@
                 if (extras != null) {
                     intent.putExtras(extras);
                 }
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 am.broadcastIntent(
                     null, intent,
                             null, null, 0, null, null, null, false, false);
@@ -3545,7 +3696,9 @@
         } else {
             // Re installation failed. Restore old information
             // Remove new pkg information
-            removePackageLI(newPackage, true);
+            if (newPackage != null) {
+                removePackageLI(newPackage, true);
+            }
             // Add back the old system package
             scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath, 
                     oldPkgSetting.resourcePath,
@@ -4084,6 +4237,7 @@
             return false;
         }
         synchronized (mPackages) {
+            grantPermissionsLP(newPkg, true);
             mSettings.writeLP();
         }
         return true;
@@ -4445,6 +4599,42 @@
         }
     }
 
+    public void replacePreferredActivity(IntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+        if (filter.countActions() != 1) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have only 1 action.");
+        }
+        if (filter.countCategories() != 1) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have only 1 category.");
+        }
+        if (filter.countDataAuthorities() != 0
+                || filter.countDataPaths() != 0
+                || filter.countDataSchemes() != 0
+                || filter.countDataTypes() != 0) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have no data authorities, " +
+                    "paths, schemes or types.");
+        }
+        synchronized (mPackages) {
+            Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
+            String action = filter.getAction(0);
+            String category = filter.getCategory(0);
+            while (it.hasNext()) {
+                PreferredActivity pa = it.next();
+                if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) {
+                    it.remove();
+                    Log.i(TAG, "Removed preferred activity " + pa.mActivity + ":");
+                    filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+                }
+            }
+            addPreferredActivity(filter, match, set, activity);
+        }
+    }
+
     public void clearPackagePreferredActivities(String packageName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
@@ -4609,6 +4799,14 @@
 
     public void systemReady() {
         mSystemReady = true;
+
+        // Read the compatibilty setting when the system is ready.
+        mCompatibilityModeEnabled = android.provider.Settings.System.getInt(
+                mContext.getContentResolver(),
+                android.provider.Settings.System.COMPATIBILITY_MODE, 1) == 1;
+        if (DEBUG_SETTINGS) {
+            Log.d(TAG, "compatibility mode:" + mCompatibilityModeEnabled);
+        }
     }
 
     public boolean isSafeMode() {
@@ -5607,24 +5805,15 @@
                     // Check to see if its a disabled system app
                     PackageSetting ps = mDisabledSysPackages.get(name);
                     if((ps != null) && ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
-                        // Could be a replaced system package
-                        // Note that if the user replaced a system app, the user has to physically
-                        // delete the new one in order to revert to the system app. So even
-                        // if the user updated the system app via an update, the user still
-                        // has to delete the one installed in the data partition in order to pick up the
-                        // new system package.
+                        // This is an updated system app with versions in both system
+                        // and data partition. Just let the most recent version
+                        // take precedence.
                         return p;
-                    } else if ((p.pkg != null) && (p.pkg.applicationInfo != null) &&
-                                ((p.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
-                        // Check for non-system apps
+                    } else if ((p.pkg != null) && (p.pkg.applicationInfo != null)) {
+                        // Let the app continue with previous uid if code path changes.
                         reportSettingsProblem(Log.WARN,
                                 "Package " + name + " codePath changed from " + p.codePath
                                 + " to " + codePath + "; Retaining data and using new code");
-                    } else {
-                        reportSettingsProblem(Log.WARN,
-                                "Package " + name + " codePath changed from " + p.codePath
-                                + " to " + codePath + "; replacing with new");
-                        p = null;
                     }
                 } else if (p.sharedUser != sharedUser) {
                     reportSettingsProblem(Log.WARN,
@@ -5709,7 +5898,7 @@
                     continue;
                 }
                 for (PackageSetting pkg:sus.packages) {
-                    if (pkg.grantedPermissions.contains (eachPerm)) {
+                    if (pkg.pkg.requestedPermissions.contains(eachPerm)) {
                         used = true;
                         break;
                     }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index c5ea5fa9..79d78ad1 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -709,7 +709,10 @@
                     p.awakeOnSet = true;
                 }
             } else {
-                mPokeLocks.remove(token);
+                PokeLock rLock = mPokeLocks.remove(token);
+                if (rLock != null) {
+                    token.unlinkToDeath(rLock, 0);
+                }
             }
 
             int oldPokey = mPokey;
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 55adabb..58f8980 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -54,7 +54,10 @@
         PROC_SPACE_TERM|PROC_OUT_LONG                   // 14: stime
     };
 
+    /** Stores user time and system time in 100ths of a second. */
     private final long[] mProcessStatsData = new long[2];
+    /** Stores user time and system time in 100ths of a second. */
+    private final long[] mSinglePidStatsData = new long[2];
 
     private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
         PROC_SPACE_TERM,
@@ -418,7 +421,18 @@
         
         return pids;
     }
-    
+
+    public long getCpuTimeForPid(int pid) {
+        final String statFile = "/proc/" + pid + "/stat";
+        final long[] statsData = mSinglePidStatsData;
+        if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
+                null, statsData, null)) {
+            long time = statsData[0] + statsData[1];
+            return time;
+        }
+        return 0;
+    }
+
     final public int getLastUserTime() {
         return mRelUserTime;
     }
diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java
new file mode 100644
index 0000000..4ac1c6e
--- /dev/null
+++ b/services/java/com/android/server/RandomBlock.java
@@ -0,0 +1,100 @@
+/*
+ * 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.util.Log;
+
+import java.io.Closeable;
+import java.io.DataOutput;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * A 4k block of random {@code byte}s.
+ */
+class RandomBlock {
+
+    private static final String TAG = "RandomBlock";
+    private static final int BLOCK_SIZE = 4096;
+    private byte[] block = new byte[BLOCK_SIZE];
+
+    private RandomBlock() { }
+
+    static RandomBlock fromFile(String filename) throws IOException {
+        Log.v(TAG, "reading from file " + filename);
+        InputStream stream = null;
+        try {
+            stream = new FileInputStream(filename);
+            return fromStream(stream);
+        } finally {
+            close(stream);
+        }
+    }
+
+    private static RandomBlock fromStream(InputStream in) throws IOException {
+        RandomBlock retval = new RandomBlock();
+        int total = 0;
+        while(total < BLOCK_SIZE) {
+            int result = in.read(retval.block, total, BLOCK_SIZE - total);
+            if (result == -1) {
+                throw new EOFException();
+            }
+            total += result;
+        }
+        return retval;
+    }
+
+    void toFile(String filename) throws IOException {
+        Log.v(TAG, "writing to file " + filename);
+        RandomAccessFile out = null;
+        try {
+            out = new RandomAccessFile(filename, "rws");
+            toDataOut(out);
+            truncateIfPossible(out);
+        } finally {
+            close(out);
+        }
+    }
+
+    private static void truncateIfPossible(RandomAccessFile f) {
+        try {
+            f.setLength(BLOCK_SIZE);
+        } catch (IOException e) {
+            // ignore this exception.  Sometimes, the file we're trying to
+            // write is a character device, such as /dev/urandom, and
+            // these character devices do not support setting the length.
+        }
+    }
+
+    private void toDataOut(DataOutput out) throws IOException {
+        out.write(block);
+    }
+
+    private static void close(Closeable c) {
+        try {
+            if (c == null) {
+                return;
+            }
+            c.close();
+        } catch (IOException e) {
+            Log.w(TAG, "IOException thrown while closing Closeable", e);
+        }
+    }
+}
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
index b2530383f..ceef39f 100644
--- a/services/java/com/android/server/SensorService.java
+++ b/services/java/com/android/server/SensorService.java
@@ -19,7 +19,7 @@
 import android.content.Context;
 import android.hardware.ISensorService;
 import android.os.Binder;
-import android.os.ParcelFileDescriptor;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.util.Config;
@@ -101,7 +101,7 @@
         _sensors_control_init();
     }
     
-    public ParcelFileDescriptor getDataChanel() throws RemoteException {
+    public Bundle getDataChannel() throws RemoteException {
         return _sensors_control_open();
     }
     
@@ -190,7 +190,7 @@
     ArrayList<Listener> mListeners = new ArrayList<Listener>();
 
     private static native int _sensors_control_init();
-    private static native ParcelFileDescriptor _sensors_control_open();
+    private static native Bundle _sensors_control_open();
     private static native boolean _sensors_control_activate(int sensor, boolean activate);
     private static native int _sensors_control_set_delay(int ms);
     private static native int _sensors_control_wake();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8b7260b..3e4d5f9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,9 @@
 
         // Critical services...
         try {
+            Log.i(TAG, "Starting Entropy Service.");
+            ServiceManager.addService("entropy", new EntropyService());
+
             Log.i(TAG, "Starting Power Manager.");
             power = new PowerManagerService();
             ServiceManager.addService(Context.POWER_SERVICE, power);
@@ -228,6 +231,14 @@
             }
 
             try {
+              Log.i(TAG, "Starting Accessibility Manager.");
+              ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
+                      new AccessibilityManagerService(context));
+            } catch (Throwable e) {
+              Log.e(TAG, "Failure starting Accessibility Manager", e);
+            }
+
+            try {
                 Log.i(TAG, "Starting Notification Manager.");
                 ServiceManager.addService(Context.NOTIFICATION_SERVICE,
                         new NotificationManagerService(context, statusBar, hardware));
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index fa54421..9f2856c 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -26,6 +26,7 @@
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -39,48 +40,71 @@
 import com.android.internal.telephony.IPhoneStateListener;
 import com.android.internal.telephony.DefaultPhoneNotifier;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneStateIntentReceiver;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.server.am.BatteryStatsService;
 
-
 /**
- * Since phone process can be restarted, this class provides a centralized
- * place that applications can register and be called back from.
+ * Since phone process can be restarted, this class provides a centralized place
+ * that applications can register and be called back from.
  */
 class TelephonyRegistry extends ITelephonyRegistry.Stub {
     private static final String TAG = "TelephonyRegistry";
 
     private static class Record {
         String pkgForDebug;
+
         IBinder binder;
+
         IPhoneStateListener callback;
+
         int events;
     }
 
     private final Context mContext;
+
     private final ArrayList<Record> mRecords = new ArrayList();
+
     private final IBatteryStats mBatteryStats;
 
     private int mCallState = TelephonyManager.CALL_STATE_IDLE;
+
     private String mCallIncomingNumber = "";
+
     private ServiceState mServiceState = new ServiceState();
-    private int mSignalStrength = -1;
+
+    private SignalStrength mSignalStrength = new SignalStrength();
+
     private boolean mMessageWaiting = false;
+
     private boolean mCallForwarding = false;
+
     private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+
     private int mDataConnectionState = TelephonyManager.DATA_CONNECTED;
+
     private boolean mDataConnectionPossible = false;
+
     private String mDataConnectionReason = "";
+
     private String mDataConnectionApn = "";
+
     private String mDataConnectionInterfaceName = "";
+
     private Bundle mCellLocation = new Bundle();
 
-    // we keep a copy of all of the sate so we can send it out when folks register for it
+    static final int PHONE_STATE_PERMISSION_MASK =
+                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
+                PhoneStateListener.LISTEN_CALL_STATE |
+                PhoneStateListener.LISTEN_DATA_ACTIVITY |
+                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
+                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
+
+    // we keep a copy of all of the state so we can send it out when folks
+    // register for it
     //
-    // In these calls we call with the lock held.  This is safe becasuse remote
-    // calls go through a oneway interface and local calls going through a handler before
-    // they get to app code.
+    // In these calls we call with the lock held. This is safe becasuse remote
+    // calls go through a oneway interface and local calls going through a
+    // handler before they get to app code.
 
     TelephonyRegistry(Context context) {
         CellLocation.getEmpty().fillInNotifierBundle(mCellLocation);
@@ -90,14 +114,11 @@
 
     public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
             boolean notifyNow) {
-        //Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events));
+        // Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" +
+        // Integer.toHexString(events));
         if (events != 0) {
-            // check permissions
-            if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
-
-            }
+            /* Checks permission and throws Security exception */
+            checkListenerPermission(events);
 
             synchronized (mRecords) {
                 // register
@@ -105,7 +126,7 @@
                 find_and_add: {
                     IBinder b = callback.asBinder();
                     final int N = mRecords.size();
-                    for (int i=0; i<N; i++) {
+                    for (int i = 0; i < N; i++) {
                         r = mRecords.get(i);
                         if (b == r.binder) {
                             break find_and_add;
@@ -125,7 +146,9 @@
                     }
                     if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                         try {
-                            r.callback.onSignalStrengthChanged(mSignalStrength);
+                            int gsmSignalStrength = mSignalStrength.getGsmSignalStrength();
+                            r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+                                    : gsmSignalStrength));
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -168,6 +191,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+                        try {
+                            r.callback.onSignalStrengthsChanged(mSignalStrength);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -177,8 +207,8 @@
 
     private void remove(IBinder binder) {
         synchronized (mRecords) {
-            final int N = mRecords.size();
-            for (int i=0; i<N; i++) {
+            final int recordCount = mRecords.size();
+            for (int i = 0; i < recordCount; i++) {
                 if (mRecords.get(i).binder == binder) {
                     mRecords.remove(i);
                     return;
@@ -188,14 +218,13 @@
     }
 
     public void notifyCallState(int state, String incomingNumber) {
-        if (!checkPhoneStatePermission("notifyCallState()")) {
+        if (!checkNotifyPermission("notifyCallState()")) {
             return;
         }
         synchronized (mRecords) {
             mCallState = state;
             mCallIncomingNumber = incomingNumber;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                     try {
@@ -210,13 +239,12 @@
     }
 
     public void notifyServiceState(ServiceState state) {
-        if (!checkPhoneStatePermission("notifyServiceState()")) {
+        if (!checkNotifyPermission("notifyServiceState()")){
             return;
-        }      
+        }
         synchronized (mRecords) {
             mServiceState = state;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                     sendServiceState(r, state);
@@ -226,35 +254,38 @@
         broadcastServiceStateChanged(state);
     }
 
-    public void notifySignalStrength(int signalStrengthASU) {
-        if (!checkPhoneStatePermission("notifySignalStrength()")) {
+    public void notifySignalStrength(SignalStrength signalStrength) {
+        if (!checkNotifyPermission("notifySignalStrength()")) {
             return;
-        }      
+        }
         synchronized (mRecords) {
-            mSignalStrength = signalStrengthASU;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            mSignalStrength = signalStrength;
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
+                if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+                    sendSignalStrength(r, signalStrength);
+                }
                 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                     try {
-                        r.callback.onSignalStrengthChanged(signalStrengthASU);
+                        int gsmSignalStrength = signalStrength.getGsmSignalStrength();
+                        r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+                                : gsmSignalStrength));
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
             }
         }
-        broadcastSignalStrengthChanged(signalStrengthASU);
+        broadcastSignalStrengthChanged(signalStrength);
     }
 
     public void notifyMessageWaitingChanged(boolean mwi) {
-        if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) {
+        if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
             return;
-        }      
+        }
         synchronized (mRecords) {
             mMessageWaiting = mwi;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
                     try {
@@ -268,13 +299,12 @@
     }
 
     public void notifyCallForwardingChanged(boolean cfi) {
-        if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) {
+        if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
             return;
-        }   
+        }
         synchronized (mRecords) {
             mCallForwarding = cfi;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
                     try {
@@ -288,13 +318,12 @@
     }
 
     public void notifyDataActivity(int state) {
-        if (!checkPhoneStatePermission("notifyDataActivity()")) {
+        if (!checkNotifyPermission("notifyDataActivity()" )) {
             return;
-        }   
+        }
         synchronized (mRecords) {
             mDataActivity = state;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
                     try {
@@ -307,19 +336,18 @@
         }
     }
 
-    public void notifyDataConnection(int state, boolean isDataConnectivityPissible,
+    public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
             String reason, String apn, String interfaceName) {
-        if (!checkPhoneStatePermission("notifyDataConnection()")) {
+        if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
-        }   
+        }
         synchronized (mRecords) {
             mDataConnectionState = state;
-            mDataConnectionPossible = isDataConnectivityPissible;
+            mDataConnectionPossible = isDataConnectivityPossible;
             mDataConnectionReason = reason;
             mDataConnectionApn = apn;
             mDataConnectionInterfaceName = interfaceName;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
                     try {
@@ -330,17 +358,17 @@
                 }
             }
         }
-        broadcastDataConnectionStateChanged(state, isDataConnectivityPissible,
-                reason, apn, interfaceName);
+        broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
+                interfaceName);
     }
 
     public void notifyDataConnectionFailed(String reason) {
-        if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) {
+        if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
-        }   
+        }
         /*
          * This is commented out because there is on onDataConnectionFailed callback
-         * on PhoneStateListener.  There should be.
+         * on PhoneStateListener. There should be
         synchronized (mRecords) {
             mDataConnectionFailedReason = reason;
             final int N = mRecords.size();
@@ -356,13 +384,12 @@
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
-        if (!checkPhoneStatePermission("notifyCellLocation()")) {
+        if (!checkNotifyPermission("notifyCellLocation()")) {
             return;
-        } 
+        }
         synchronized (mRecords) {
             mCellLocation = cellLocation;
-            final int N = mRecords.size();
-            for (int i=N-1; i>=0; i--) {
+            for (int i = mRecords.size() - 1; i >= 0; i--) {
                 Record r = mRecords.get(i);
                 if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
                     sendCellLocation(r, cellLocation);
@@ -371,11 +398,9 @@
         }
     }
 
-    //
-    // the new callback broadcasting
-    //
-    // copy the service state object so they can't mess it up in the local calls
-    // 
+    /**
+     * Copy the service state object so they can't mess it up in the local calls
+     */
     public void sendServiceState(Record r, ServiceState state) {
         try {
             r.callback.onServiceStateChanged(new ServiceState(state));
@@ -384,7 +409,7 @@
         }
     }
 
-    public void sendCellLocation(Record r, Bundle cellLocation) {
+    private void sendCellLocation(Record r, Bundle cellLocation) {
         try {
             r.callback.onCellLocationChanged(new Bundle(cellLocation));
         } catch (RemoteException ex) {
@@ -392,18 +417,24 @@
         }
     }
 
+    private void sendSignalStrength(Record r, SignalStrength signalStrength) {
+        try {
+            r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
+        } catch (RemoteException ex) {
+            remove(r.binder);
+        }
+    }
 
     @Override
     public 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 telephony.registry from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
             return;
         }
         synchronized (mRecords) {
-            final int N = mRecords.size();
+            final int recordCount = mRecords.size();
             pw.println("last known state:");
             pw.println("  mCallState=" + mCallState);
             pw.println("  mCallIncomingNumber=" + mCallIncomingNumber);
@@ -418,20 +449,28 @@
             pw.println("  mDataConnectionApn=" + mDataConnectionApn);
             pw.println("  mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
             pw.println("  mCellLocation=" + mCellLocation);
-            pw.println("registrations: count=" + N);
-            for (int i=0; i<N; i++) {
+            pw.println("registrations: count=" + recordCount);
+            for (int i = 0; i < recordCount; i++) {
                 Record r = mRecords.get(i);
                 pw.println("  " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
             }
         }
     }
 
-    
     //
     // the legacy intent broadcasting
     //
 
     private void broadcastServiceStateChanged(ServiceState state) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mBatteryStats.noteAirplaneMode(state.getState() == ServiceState.STATE_POWER_OFF);
+        } catch (RemoteException re) {
+            // Can't do much
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
         Bundle data = new Bundle();
         state.fillInNotifierBundle(data);
@@ -439,17 +478,20 @@
         mContext.sendStickyBroadcast(intent);
     }
 
-    private void broadcastSignalStrengthChanged(int asu) {
+    private void broadcastSignalStrengthChanged(SignalStrength signalStrength) {
         long ident = Binder.clearCallingIdentity();
         try {
-            mBatteryStats.notePhoneSignalStrength(asu);
+            mBatteryStats.notePhoneSignalStrength(signalStrength);
         } catch (RemoteException e) {
+            /* The remote entity disappeared, we can safely ignore the exception. */
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        
+
         Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
-        intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu);
+        Bundle data = new Bundle();
+        signalStrength.fillInNotifierBundle(data);
+        intent.putExtras(data);
         mContext.sendStickyBroadcast(intent);
     }
 
@@ -462,13 +504,13 @@
                 mBatteryStats.notePhoneOn();
             }
         } catch (RemoteException e) {
+            /* The remote entity disappeared, we can safely ignore the exception. */
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        
+
         Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
-        intent.putExtra(Phone.STATE_KEY,
-                DefaultPhoneNotifier.convertCallState(state).toString());
+        intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString());
         if (!TextUtils.isEmpty(incomingNumber)) {
             intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
         }
@@ -498,16 +540,28 @@
         intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
         mContext.sendStickyBroadcast(intent);
     }
-    
-    private boolean checkPhoneStatePermission(String method) {
+
+    private boolean checkNotifyPermission(String method) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED) {
             return true;
         }
         String msg = "Modify Phone State Permission Denial: " + method + " from pid="
-                + Binder.getCallingPid()
-                + ", uid=" + Binder.getCallingUid();
+                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
         Log.w(TAG, msg);
         return false;
     }
+
+    private void checkListenerPermission(int events) {
+        if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
+
+        }
+
+        if ((events & PHONE_STATE_PERMISSION_MASK) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PHONE_STATE, null);
+        }
+    }
 }
diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java
index 5532894..d921baf 100644
--- a/services/java/com/android/server/WallpaperService.java
+++ b/services/java/com/android/server/WallpaperService.java
@@ -18,8 +18,10 @@
 
 import static android.os.FileObserver.*;
 import static android.os.ParcelFileDescriptor.*;
+
 import android.app.IWallpaperService;
 import android.app.IWallpaperServiceCallback;
+import android.backup.BackupManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -154,7 +156,16 @@
     public ParcelFileDescriptor setWallpaper() {
         checkPermission(android.Manifest.permission.SET_WALLPAPER);
         try {
-            return ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE);
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+                    MODE_CREATE|MODE_READ_WRITE);
+
+            // changing the wallpaper means we'll need to back up the new one
+            long origId = Binder.clearCallingIdentity();
+            BackupManager bm = new BackupManager(mContext);
+            bm.dataChanged();
+            Binder.restoreCallingIdentity(origId);
+
+            return fd;
         } catch (FileNotFoundException e) {
             if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
         }
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index fef3598..68bf4fb 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -504,6 +504,7 @@
                 if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
                         mPhonePss)) {
                     // Just kill the phone process and let it restart.
+                    Log.i(TAG, "Watchdog is killing the phone process");
                     Process.killProcess(mPhonePid);
                 }
             } else {
@@ -848,6 +849,7 @@
 
             // Only kill the process if the debugger is not attached.
             if (!Debug.isDebuggerConnected()) {
+                Log.i(TAG, "Watchdog is killing the system process");
                 Process.killProcess(Process.myPid());
             }
         }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 348f0a1..a940af3 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -38,6 +38,7 @@
 import android.net.wifi.WifiStateTracker;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.SupplicantState;
 import android.net.NetworkStateTracker;
 import android.net.DhcpInfo;
 import android.os.Binder;
@@ -49,6 +50,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.text.TextUtils;
@@ -64,6 +66,7 @@
 import java.io.PrintWriter;
 
 import com.android.internal.app.IBatteryStats;
+import android.backup.IBackupManager;
 import com.android.server.am.BatteryStatsService;
 
 /**
@@ -96,8 +99,8 @@
     private int mScanLocksAcquired;
     private int mScanLocksReleased;
 
-    private final List<WifiMulticaster> mMulticasters =
-            new ArrayList<WifiMulticaster>();
+    private final List<Multicaster> mMulticasters =
+            new ArrayList<Multicaster>();
     private int mMulticastEnabled;
     private int mMulticastDisabled;
 
@@ -588,6 +591,12 @@
 
     }
 
+    private void enforceMulticastChangePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
+                "WifiService");
+    }
+
     /**
      * see {@link WifiManager#getWifiState()}
      * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
@@ -1054,6 +1063,94 @@
                 break setVariables;
             }
 
+            if ((config.eap != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.eapVarName,
+                    config.eap)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set eap: "+
+                          config.eap);
+                }
+                break setVariables;
+            }
+
+            if ((config.identity != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.identityVarName,
+                    config.identity)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set identity: "+
+                          config.identity);
+                }
+                break setVariables;
+            }
+
+            if ((config.anonymousIdentity != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.anonymousIdentityVarName,
+                    config.anonymousIdentity)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set anonymousIdentity: "+
+                          config.anonymousIdentity);
+                }
+                break setVariables;
+            }
+
+            if ((config.password != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.passwordVarName,
+                    config.password)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set password: "+
+                          config.password);
+                }
+                break setVariables;
+            }
+
+            if ((config.clientCert != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.clientCertVarName,
+                    config.clientCert)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set clientCert: "+
+                          config.clientCert);
+                }
+                break setVariables;
+            }
+
+            if ((config.caCert != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.caCertVarName,
+                    config.caCert)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set caCert: "+
+                          config.caCert);
+                }
+                break setVariables;
+            }
+
+            if ((config.privateKey != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.privateKeyVarName,
+                    config.privateKey)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set privateKey: "+
+                          config.privateKey);
+                }
+                break setVariables;
+            }
+
+            if ((config.privateKeyPasswd != null) && !WifiNative.setNetworkVariableCommand(
+                    netId,
+                    WifiConfiguration.privateKeyPasswdVarName,
+                    config.privateKeyPasswd)) {
+                if (DBG) {
+                    Log.d(TAG, config.SSID + ": failed to set privateKeyPasswd: "+
+                          config.privateKeyPasswd);
+                }
+                break setVariables;
+            }
+
             return netId;
         }
 
@@ -1353,6 +1450,16 @@
                 }
             }
         }
+        // Inform the backup manager about a data change
+        IBackupManager ibm = IBackupManager.Stub.asInterface(
+                ServiceManager.getService(Context.BACKUP_SERVICE));
+        if (ibm != null) {
+            try {
+                ibm.dataChanged("com.android.providers.settings");
+            } catch (Exception e) {
+                // Try again later
+            }
+        }
         return result;
     }
 
@@ -1449,10 +1556,12 @@
                     Settings.System.getInt(mContext.getContentResolver(),
                                            Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
+                Log.d(TAG, "ACTION_SCREEN_ON");
                 mAlarmManager.cancel(mIdleIntent);
                 mDeviceIdle = false;
                 mScreenOff = false;
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+                Log.d(TAG, "ACTION_SCREEN_OFF");
                 mScreenOff = true;
                 /*
                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
@@ -1461,12 +1570,21 @@
                  * or plugged in to AC).
                  */
                 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
-                    long triggerTime = System.currentTimeMillis() + idleMillis;
-                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
+                    WifiInfo info = mWifiStateTracker.requestConnectionInfo();
+                    if (info.getSupplicantState() != SupplicantState.COMPLETED) {
+                        // do not keep Wifi awake when screen is off if Wifi is not associated
+                        mDeviceIdle = true;
+                        updateWifiState();
+                    } else {
+                        long triggerTime = System.currentTimeMillis() + idleMillis;
+                        Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
+                    }
                 }
                 /* we can return now -- there's nothing to do until we get the idle intent back */
                 return;
             } else if (action.equals(ACTION_DEVICE_IDLE)) {
+                Log.d(TAG, "got ACTION_DEVICE_IDLE");
                 mDeviceIdle = true;
             } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                 /*
@@ -1477,9 +1595,11 @@
                  * the already-set timer.
                  */
                 int pluggedType = intent.getIntExtra("plugged", 0);
+                Log.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
                 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
                         !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
                     long triggerTime = System.currentTimeMillis() + idleMillis;
+                    Log.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                     mPluggedType = pluggedType;
                     return;
@@ -1732,7 +1852,7 @@
         }
     }
 
-    private class WifiLock extends WifiDeathRecipient {
+    private class WifiLock extends DeathRecipient {
         WifiLock(int lockMode, String tag, IBinder binder) {
             super(lockMode, tag, binder);
         }
@@ -1780,7 +1900,9 @@
         private WifiLock removeLock(IBinder binder) {
             int index = findLockByBinder(binder);
             if (index >= 0) {
-                return mList.remove(index);
+                WifiLock ret = mList.remove(index);
+                ret.unlinkDeathRecipient();
+                return ret;
             } else {
                 return null;
             }
@@ -1875,13 +1997,13 @@
         return hadLock;
     }
 
-    private abstract class WifiDeathRecipient
+    private abstract class DeathRecipient
             implements IBinder.DeathRecipient {
         String mTag;
         int mMode;
         IBinder mBinder;
 
-        WifiDeathRecipient(int mode, String tag, IBinder binder) {
+        DeathRecipient(int mode, String tag, IBinder binder) {
             super();
             mTag = tag;
             mMode = mode;
@@ -1892,15 +2014,19 @@
                 binderDied();
             }
         }
+
+        void unlinkDeathRecipient() {
+            mBinder.unlinkToDeath(this, 0);
+        }
     }
 
-    private class WifiMulticaster extends WifiDeathRecipient {
-        WifiMulticaster(String tag, IBinder binder) {
+    private class Multicaster extends DeathRecipient {
+        Multicaster(String tag, IBinder binder) {
             super(Binder.getCallingUid(), tag, binder);
         }
 
         public void binderDied() {
-            Log.e(TAG, "WifiMulticaster binderDied");
+            Log.e(TAG, "Multicaster binderDied");
             synchronized (mMulticasters) {
                 int i = mMulticasters.indexOf(this);
                 if (i != -1) {
@@ -1910,7 +2036,7 @@
         }
 
         public String toString() {
-            return "WifiMulticaster{" + mTag + " binder=" + mBinder + "}";
+            return "Multicaster{" + mTag + " binder=" + mBinder + "}";
         }
 
         public int getUid() {
@@ -1918,12 +2044,12 @@
         }
     }
 
-    public void enableWifiMulticast(IBinder binder, String tag) {
-        enforceChangePermission();
+    public void acquireMulticastLock(IBinder binder, String tag) {
+        enforceMulticastChangePermission();
 
         synchronized (mMulticasters) {
             mMulticastEnabled++;
-            mMulticasters.add(new WifiMulticaster(tag, binder));
+            mMulticasters.add(new Multicaster(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
@@ -1941,15 +2067,15 @@
         }
     }
 
-    public void disableWifiMulticast() {
-        enforceChangePermission();
+    public void releaseMulticastLock() {
+        enforceMulticastChangePermission();
 
         int uid = Binder.getCallingUid();
         synchronized (mMulticasters) {
             mMulticastDisabled++;
             int size = mMulticasters.size();
             for (int i = size - 1; i >= 0; i--) {
-                WifiMulticaster m = mMulticasters.get(i);
+                Multicaster m = mMulticasters.get(i);
                 if ((m != null) && (m.getUid() == uid)) {
                     removeMulticasterLocked(i, uid);
                 }
@@ -1959,7 +2085,10 @@
 
     private void removeMulticasterLocked(int i, int uid)
     {
-        mMulticasters.remove(i);
+        Multicaster removed = mMulticasters.remove(i);
+        if (removed != null) {
+            removed.unlinkDeathRecipient();
+        }
         if (mMulticasters.size() == 0) {
             WifiNative.startPacketFiltering();
         }
@@ -1973,7 +2102,7 @@
         }
     }
 
-    public boolean isWifiMulticastEnabled() {
+    public boolean isMulticastEnabled() {
         enforceAccessPermission();
 
         synchronized (mMulticasters) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 3fa5baf..2dd70ef 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -77,6 +77,7 @@
 import android.os.SystemProperties;
 import android.os.TokenWatcher;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -133,16 +134,16 @@
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_REORDER = false;
     static final boolean SHOW_TRANSACTIONS = false;
-    
+
     static final boolean PROFILE_ORIENTATION = false;
     static final boolean BLUR = true;
     static final boolean localLOGV = DEBUG;
-    
+
     static final int LOG_WM_NO_SURFACE_MEMORY = 31000;
-    
+
     /** How long to wait for first key repeat, in milliseconds */
     static final int KEY_REPEAT_FIRST_DELAY = 750;
-    
+
     /** How long to wait for subsequent key repeats, in milliseconds */
     static final int KEY_REPEAT_DELAY = 50;
 
@@ -150,16 +151,16 @@
      * for multiple windows of the same type and Z-ordering adjustment
      * with TYPE_LAYER_OFFSET. */
     static final int TYPE_LAYER_MULTIPLIER = 10000;
-    
+
     /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
      * or below others in the same layer. */
     static final int TYPE_LAYER_OFFSET = 1000;
-    
+
     /** How much to increment the layer for each window, to reserve room
      * for effect surfaces between them.
      */
     static final int WINDOW_LAYER_MULTIPLIER = 5;
-    
+
     /** The maximum length we will accept for a loaded animation duration:
      * this is 10 seconds.
      */
@@ -173,21 +174,25 @@
     /** Adjustment to time to perform a dim, to make it more dramatic.
      */
     static final int DIM_DURATION_MULTIPLIER = 6;
+
+    static final int INJECT_FAILED = 0;
+    static final int INJECT_SUCCEEDED = 1;
+    static final int INJECT_NO_PERMISSION = -1;
     
     static final int UPDATE_FOCUS_NORMAL = 0;
     static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
     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;
@@ -195,10 +200,11 @@
     // Last time we call user activity
     long mLastUserActivityCallTime = 0;
 
-    // Last time we updated battery stats 
+    // Last time we updated battery stats
     long mLastBatteryStatsCallTime = 0;
-    
+
     private static final String SYSTEM_SECURE = "ro.secure";
+    private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
     /**
      * Condition waited on by {@link #reenableKeyguard} to know the call to
@@ -224,20 +230,20 @@
     final Context mContext;
 
     final boolean mHaveInputMethods;
-    
+
     final boolean mLimitedAlphaCompositing;
-    
+
     final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
 
     final IActivityManager mActivityManager;
-    
+
     final IBatteryStats mBatteryStats;
-    
+
     /**
      * All currently active sessions with clients.
      */
     final HashSet<Session> mSessions = new HashSet<Session>();
-    
+
     /**
      * Mapping from an IWindow IBinder to the server's Window object.
      * This is also used as the lock for all of our state.
@@ -255,7 +261,7 @@
      * over them.
      */
     final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();
-    
+
     /**
      * Window tokens that are in the process of exiting, but still
      * on screen for animations.
@@ -314,9 +320,9 @@
      * list or contain windows that need to be force removed.
      */
     ArrayList<WindowState> mForceRemoves;
-    
+
     IInputMethodManager mInputMethodManager;
-    
+
     SurfaceSession mFxSession;
     Surface mDimSurface;
     boolean mDimShown;
@@ -326,9 +332,9 @@
     long mLastDimAnimTime;
     Surface mBlurSurface;
     boolean mBlurShown;
-    
+
     int mTransactionSequence = 0;
-    
+
     final float[] mTmpFloats = new float[9];
 
     boolean mSafeMode;
@@ -340,7 +346,7 @@
     int mLastRotationFlags;
     ArrayList<IRotationWatcher> mRotationWatchers
             = new ArrayList<IRotationWatcher>();
-    
+
     boolean mLayoutNeeded = true;
     boolean mAnimationPending = false;
     boolean mDisplayFrozen = false;
@@ -352,7 +358,7 @@
     // perform a rotation animation when turning off shows the lock screen which
     // changes the orientation.
     PowerManager.WakeLock mScreenFrozenLock;
-    
+
     // State management of app transitions.  When we are preparing for a
     // transition, mNextAppTransition will be the kind of transition to
     // perform or TRANSIT_NONE if we are not waiting.  If we are waiting,
@@ -365,40 +371,40 @@
     boolean mSkipAppTransitionAnimation = false;
     final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
     final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
-    
+
     //flag to detect fat touch events
     boolean mFatTouch = false;
     Display mDisplay;
-    
+
     H mH = new H();
 
     WindowState mCurrentFocus = null;
     WindowState mLastFocus = null;
-    
+
     // This just indicates the window the input method is on top of, not
     // necessarily the window its input is going to.
     WindowState mInputMethodTarget = null;
     WindowState mUpcomingInputMethodTarget = null;
     boolean mInputMethodTargetWaitingAnim;
     int mInputMethodAnimLayerAdjustment;
-    
+
     WindowState mInputMethodWindow = null;
     final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
 
     AppWindowToken mFocusedApp = null;
 
     PowerManagerService mPowerManager;
-    
+
     float mWindowAnimationScale = 1.0f;
     float mTransitionAnimationScale = 1.0f;
-    
+
     final KeyWaiter mKeyWaiter = new KeyWaiter();
     final KeyQ mQueue;
     final InputDispatcherThread mInputThread;
 
     // Who is holding the screen on.
     Session mHoldingScreenOn;
-    
+
     /**
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
@@ -408,14 +414,15 @@
     private ViewServer mViewServer;
 
     final Rect mTempRect = new Rect();
-    
+
     final Configuration mTempConfiguration = new Configuration();
+    int screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
     
     public static WindowManagerService main(Context context,
             PowerManagerService pm, boolean haveInputMethods) {
         WMThread thr = new WMThread(context, pm, haveInputMethods);
         thr.start();
-        
+
         synchronized (thr) {
             while (thr.mService == null) {
                 try {
@@ -424,17 +431,17 @@
                 }
             }
         }
-        
+
         return thr.mService;
     }
-    
+
     static class WMThread extends Thread {
         WindowManagerService mService;
-        
+
         private final Context mContext;
         private final PowerManagerService mPM;
         private final boolean mHaveInputMethods;
-        
+
         public WMThread(Context context, PowerManagerService pm,
                 boolean haveInputMethods) {
             super("WindowManager");
@@ -442,19 +449,19 @@
             mPM = pm;
             mHaveInputMethods = haveInputMethods;
         }
-        
+
         public void run() {
             Looper.prepare();
             WindowManagerService s = new WindowManagerService(mContext, mPM,
                     mHaveInputMethods);
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_DISPLAY);
-            
+
             synchronized (this) {
                 mService = s;
                 notifyAll();
             }
-            
+
             Looper.loop();
         }
     }
@@ -465,7 +472,7 @@
         private final Context mContext;
         private final PowerManagerService mPM;
         boolean mRunning = false;
-        
+
         public PolicyThread(WindowManagerPolicy policy,
                 WindowManagerService service, Context context,
                 PowerManagerService pm) {
@@ -475,7 +482,7 @@
             mContext = context;
             mPM = pm;
         }
-        
+
         public void run() {
             Looper.prepare();
             //Looper.myLooper().setMessageLogging(new LogPrinter(
@@ -483,12 +490,12 @@
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
             mPolicy.init(mContext, mService, mPM);
-            
+
             synchronized (this) {
                 mRunning = true;
                 notifyAll();
             }
-            
+
             Looper.loop();
         }
     }
@@ -499,7 +506,7 @@
         mHaveInputMethods = haveInputMethods;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_sf_limitedAlpha);
-        
+
         mPowerManager = pm;
         mPowerManager.setPolicy(mPolicy);
         PowerManager pmc = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -515,14 +522,14 @@
                 Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
         mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
                 Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
-        
+
         mQueue = new KeyQ();
 
         mInputThread = new InputDispatcherThread();
-        
+
         PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
         thr.start();
-        
+
         synchronized (thr) {
             while (!thr.mRunning) {
                 try {
@@ -531,9 +538,9 @@
                 }
             }
         }
-        
+
         mInputThread.start();
-        
+
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
     }
@@ -586,12 +593,12 @@
         }
         return -1;
     }
-    
+
     private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
         final IWindow client = win.mClient;
         final WindowToken token = win.mToken;
         final ArrayList localmWindows = mWindows;
-        
+
         final int N = localmWindows.size();
         final WindowState attached = win.mAttachedWindow;
         int i;
@@ -616,12 +623,12 @@
                         } else {
                             int newIdx =  findIdxBasedOnAppTokens(win);
                             if(newIdx != -1) {
-                                //there is a window above this one associated with the same 
-                                //apptoken note that the window could be a floating window 
-                                //that was created later or a window at the top of the list of 
+                                //there is a window above this one associated with the same
+                                //apptoken note that the window could be a floating window
+                                //that was created later or a window at the top of the list of
                                 //windows associated with this token.
                                 localmWindows.add(newIdx+1, win);
-                            } 
+                            }
                         }
                     }
                 } else {
@@ -647,7 +654,7 @@
                     // we need to look some more.
                     if (pos != null) {
                         // Move behind any windows attached to this one.
-                        WindowToken atoken = 
+                        WindowToken atoken =
                             mTokenMap.get(((WindowState)pos).mClient.asBinder());
                         if (atoken != null) {
                             final int NC = atoken.windows.size();
@@ -770,12 +777,12 @@
                 }
             }
         }
-        
+
         if (win.mAppToken != null && addToToken) {
             win.mAppToken.allAppWindows.add(win);
         }
     }
-    
+
     static boolean canBeImeTarget(WindowState w) {
         final int fl = w.mAttrs.flags
                 & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
@@ -784,7 +791,7 @@
         }
         return false;
     }
-    
+
     int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
         final ArrayList localmWindows = mWindows;
         final int N = localmWindows.size();
@@ -793,12 +800,12 @@
         while (i > 0) {
             i--;
             w = (WindowState)localmWindows.get(i);
-            
+
             //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x"
             //        + Integer.toHexString(w.mAttrs.flags));
             if (canBeImeTarget(w)) {
                 //Log.i(TAG, "Putting input method here!");
-                
+
                 // Yet more tricksyness!  If this window is a "starting"
                 // window, we do actually want to be on top of it, but
                 // it is not -really- where input will go.  So if the caller
@@ -816,16 +823,16 @@
                 break;
             }
         }
-        
+
         mUpcomingInputMethodTarget = w;
-        
+
         if (DEBUG_INPUT_METHOD) Log.v(TAG, "Desired input method target="
                 + w + " willMove=" + willMove);
-        
+
         if (willMove && w != null) {
             final WindowState curTarget = mInputMethodTarget;
             if (curTarget != null && curTarget.mAppToken != null) {
-                
+
                 // Now some fun for dealing with window animations that
                 // modify the Z order.  We need to look at all windows below
                 // the current target that are in this app, finding the highest
@@ -851,14 +858,14 @@
                         pos--;
                     }
                 }
-                
+
                 if (highestTarget != null) {
-                    if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" 
+                    if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition="
                             + mNextAppTransition + " " + highestTarget
                             + " animating=" + highestTarget.isAnimating()
                             + " layer=" + highestTarget.mAnimLayer
                             + " new layer=" + w.mAnimLayer);
-                    
+
                     if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
                         // If we are currently setting up for an animation,
                         // hold everything until we can find out what will happen.
@@ -877,7 +884,7 @@
                 }
             }
         }
-        
+
         //Log.i(TAG, "Placing input method @" + (i+1));
         if (w != null) {
             if (willMove) {
@@ -904,7 +911,7 @@
         }
         return -1;
     }
-    
+
     void addInputMethodWindowToListLocked(WindowState win) {
         int pos = findDesiredInputMethodWindowIndexLocked(true);
         if (pos >= 0) {
@@ -917,7 +924,7 @@
         addWindowToListInOrderLocked(win, true);
         moveInputMethodDialogsLocked(pos);
     }
-    
+
     void setInputMethodAnimLayerAdjustment(int adj) {
         if (DEBUG_LAYERS) Log.v(TAG, "Setting im layer adj to " + adj);
         mInputMethodAnimLayerAdjustment = adj;
@@ -944,7 +951,7 @@
                     + " anim layer: " + imw.mAnimLayer);
         }
     }
-    
+
     private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
         int wpos = mWindows.indexOf(win);
         if (wpos >= 0) {
@@ -963,7 +970,7 @@
         }
         return interestingPos;
     }
-    
+
     private void reAddWindowToListInOrderLocked(WindowState win) {
         addWindowToListInOrderLocked(win, false);
         // This is a hack to get all of the child windows added as well
@@ -975,7 +982,7 @@
             reAddWindowLocked(wpos, win);
         }
     }
-    
+
     void logWindowList(String prefix) {
         int N = mWindows.size();
         while (N > 0) {
@@ -983,10 +990,10 @@
             Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N));
         }
     }
-    
+
     void moveInputMethodDialogsLocked(int pos) {
         ArrayList<WindowState> dialogs = mInputMethodDialogs;
-        
+
         final int N = dialogs.size();
         if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos);
         for (int i=0; i<N; i++) {
@@ -996,7 +1003,7 @@
             Log.v(TAG, "Window list w/pos=" + pos);
             logWindowList("  ");
         }
-        
+
         if (pos >= 0) {
             final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
             if (pos < mWindows.size()) {
@@ -1027,25 +1034,25 @@
             }
         }
     }
-    
+
     boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) {
         final WindowState imWin = mInputMethodWindow;
         final int DN = mInputMethodDialogs.size();
         if (imWin == null && DN == 0) {
             return false;
         }
-        
+
         int imPos = findDesiredInputMethodWindowIndexLocked(true);
         if (imPos >= 0) {
             // In this case, the input method windows are to be placed
             // immediately above the window they are targeting.
-            
+
             // First check to see if the input method windows are already
             // located here, and contiguous.
             final int N = mWindows.size();
             WindowState firstImWin = imPos < N
                     ? (WindowState)mWindows.get(imPos) : null;
-                    
+
             // Figure out the actual input method window that should be
             // at the bottom of their stack.
             WindowState baseImWin = imWin != null
@@ -1054,7 +1061,7 @@
                 WindowState cw = (WindowState)baseImWin.mChildWindows.get(0);
                 if (cw.mSubLayer < 0) baseImWin = cw;
             }
-            
+
             if (firstImWin == baseImWin) {
                 // The windows haven't moved...  but are they still contiguous?
                 // First find the top IM window.
@@ -1078,7 +1085,7 @@
                     return false;
                 }
             }
-            
+
             if (imWin != null) {
                 if (DEBUG_INPUT_METHOD) {
                     Log.v(TAG, "Moving IM from " + imPos);
@@ -1099,11 +1106,11 @@
             } else {
                 moveInputMethodDialogsLocked(imPos);
             }
-            
+
         } else {
             // In this case, the input method windows go in a fixed layer,
             // because they aren't currently associated with a focus window.
-            
+
             if (imWin != null) {
                 if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos);
                 tmpRemoveWindowLocked(0, imWin);
@@ -1117,20 +1124,20 @@
             } else {
                 moveInputMethodDialogsLocked(-1);;
             }
-            
+
         }
-        
+
         if (needAssignLayers) {
             assignLayersLocked();
         }
-        
+
         return true;
     }
-    
+
     void adjustInputMethodDialogsLocked() {
         moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
     }
-    
+
     public int addWindow(Session session, IWindow client,
             WindowManager.LayoutParams attrs, int viewVisibility,
             Rect outContentInsets) {
@@ -1138,11 +1145,11 @@
         if (res != WindowManagerImpl.ADD_OKAY) {
             return res;
         }
-        
+
         boolean reportNewConfig = false;
         WindowState attachedWindow = null;
         WindowState win = null;
-        
+
         synchronized(mWindowMap) {
             // Instantiating a Display requires talking with the simulator,
             // so don't do it until we know the system is mostly up and
@@ -1153,14 +1160,14 @@
                 mQueue.setDisplay(mDisplay);
                 reportNewConfig = true;
             }
-            
+
             if (mWindowMap.containsKey(client.asBinder())) {
                 Log.w(TAG, "Window " + client + " is already added");
                 return WindowManagerImpl.ADD_DUPLICATE_ADD;
             }
 
             if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) {
-                attachedWindow = windowForClientLocked(null, attrs.token); 
+                attachedWindow = windowForClientLocked(null, attrs.token);
                 if (attachedWindow == null) {
                     Log.w(TAG, "Attempted to add window with token that is not a window: "
                           + attrs.token + ".  Aborting.");
@@ -1227,7 +1234,7 @@
             }
 
             mPolicy.adjustWindowParamsLw(win.mAttrs);
-            
+
             res = mPolicy.prepareAddWindowLw(win, attrs);
             if (res != WindowManagerImpl.ADD_OKAY) {
                 return res;
@@ -1236,9 +1243,9 @@
             // From now on, no exceptions or errors allowed!
 
             res = WindowManagerImpl.ADD_OKAY;
-            
+
             final long origId = Binder.clearCallingIdentity();
-            
+
             if (addToken) {
                 mTokenMap.put(attrs.token, token);
                 mTokenList.add(token);
@@ -1252,7 +1259,7 @@
             }
 
             boolean imMayMove = true;
-            
+
             if (attrs.type == TYPE_INPUT_METHOD) {
                 mInputMethodWindow = win;
                 addInputMethodWindowToListLocked(win);
@@ -1265,18 +1272,18 @@
             } else {
                 addWindowToListInOrderLocked(win, true);
             }
-            
+
             win.mEnterAnimationPending = true;
-            
+
             mPolicy.getContentInsetHintLw(attrs, outContentInsets);
-            
+
             if (mInTouchMode) {
                 res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE;
             }
             if (win == null || win.mAppToken == null || !win.mAppToken.clientHidden) {
                 res |= WindowManagerImpl.ADD_FLAG_APP_VISIBLE;
             }
-            
+
             boolean focusChanged = false;
             if (win.canReceiveKeys()) {
                 if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
@@ -1284,15 +1291,15 @@
                     imMayMove = false;
                 }
             }
-            
+
             if (imMayMove) {
-                moveInputMethodWindowsIfNeededLocked(false);                
+                moveInputMethodWindowsIfNeededLocked(false);
             }
-            
+
             assignLayersLocked();
             // Don't do layout here, the window must call
             // relayout to be displayed, so we'll do it there.
-            
+
             //dump();
 
             if (focusChanged) {
@@ -1300,7 +1307,6 @@
                     mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
                 }
             }
-            
             if (localLOGV) Log.v(
                 TAG, "New client " + client.asBinder()
                 + ": window=" + win);
@@ -1317,16 +1323,16 @@
             // Update Orientation after adding a window, only if the window needs to be
             // displayed right away
             if (win.isVisibleOrAdding()) {
-                if (updateOrientationFromAppTokens(null, null) != null) {
+                if (updateOrientationFromAppTokensUnchecked(null, null) != null) {
                     sendNewConfiguration();
                 }
             }
         }
         Binder.restoreCallingIdentity(origId);
-        
+
         return res;
     }
-    
+
     public void removeWindow(Session session, IWindow client) {
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client);
@@ -1336,7 +1342,7 @@
             removeWindowLocked(session, win);
         }
     }
-    
+
     public void removeWindowLocked(Session session, WindowState win) {
 
         if (localLOGV || DEBUG_FOCUS) Log.v(
@@ -1346,7 +1352,7 @@
             + ", surface=" + win.mSurface);
 
         final long origId = Binder.clearCallingIdentity();
-        
+
         if (DEBUG_APP_TRANSITIONS) Log.v(
                 TAG, "Remove " + win + ": mSurface=" + win.mSurface
                 + " mExiting=" + win.mExiting
@@ -1366,7 +1372,7 @@
             // If we are not currently running the exit animation, we
             // need to see about starting one.
             if (wasVisible=win.isWinVisibleLw()) {
-                
+
                 int transit = WindowManagerPolicy.TRANSIT_EXIT;
                 if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
                     transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
@@ -1403,17 +1409,17 @@
         updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
         Binder.restoreCallingIdentity(origId);
     }
-    
+
     private void removeWindowInnerLocked(Session session, WindowState win) {
         mKeyWaiter.releasePendingPointerLocked(win.mSession);
         mKeyWaiter.releasePendingTrackballLocked(win.mSession);
-        
+
         win.mRemoved = true;
-        
+
         if (mInputMethodTarget == win) {
             moveInputMethodWindowsIfNeededLocked(false);
         }
-        
+
         mPolicy.removeWindowLw(win);
         win.removeLocked();
 
@@ -1425,7 +1431,7 @@
         } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
             mInputMethodDialogs.remove(win);
         }
-        
+
         final WindowToken token = win.mToken;
         final AppWindowToken atoken = win.mAppToken;
         token.windows.remove(win);
@@ -1462,7 +1468,7 @@
                 mH.sendMessage(m);
             }
         }
-        
+
         if (!mInLayout) {
             assignLayersLocked();
             mLayoutNeeded = true;
@@ -1493,7 +1499,7 @@
     }
 
     void setInsetsWindow(Session session, IWindow client,
-            int touchableInsets, Rect contentInsets, 
+            int touchableInsets, Rect contentInsets,
             Rect visibleInsets) {
         long origId = Binder.clearCallingIdentity();
         try {
@@ -1512,7 +1518,7 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
-    
+
     public void getWindowDisplayFrame(Session session, IWindow client,
             Rect outDisplayFrame) {
         synchronized(mWindowMap) {
@@ -1534,7 +1540,7 @@
         boolean inTouchMode;
         Configuration newConfig = null;
         long origId = Binder.clearCallingIdentity();
-        
+
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client);
             if (win == null) {
@@ -1546,7 +1552,7 @@
             if (attrs != null) {
                 mPolicy.adjustWindowParamsLw(attrs);
             }
-            
+
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
@@ -1578,11 +1584,11 @@
             boolean imMayMove = (flagChanges&(
                     WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
-            
+
             boolean focusMayChange = win.mViewVisibility != viewVisibility
                     || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
                     || (!win.mRelayoutCalled);
-            
+
             win.mRelayoutCalled = true;
             final int oldVisibility = win.mViewVisibility;
             win.mViewVisibility = viewVisibility;
@@ -1670,17 +1676,17 @@
                 }
                 //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
             }
-            
+
             // updateFocusedWindowLocked() already assigned layers so we only need to
             // reassign them at this point if the IM window state gets shuffled
             boolean assignLayers = false;
-            
+
             if (imMayMove) {
                 if (moveInputMethodWindowsIfNeededLocked(false)) {
                     assignLayers = true;
                 }
             }
-                
+
             mLayoutNeeded = true;
             win.mGivenInsetsPending = insetsPending;
             if (assignLayers) {
@@ -1696,7 +1702,7 @@
             outVisibleInsets.set(win.mVisibleInsets);
             if (localLOGV) Log.v(
                 TAG, "Relayout given client " + client.asBinder()
-                + ", requestedWidth=" + requestedWidth 
+                + ", requestedWidth=" + requestedWidth
                 + ", requestedHeight=" + requestedHeight
                 + ", viewVisibility=" + viewVisibility
                 + "\nRelayout returning frame=" + outFrame
@@ -1711,9 +1717,9 @@
         if (newConfig != null) {
             sendNewConfiguration();
         }
-        
+
         Binder.restoreCallingIdentity(origId);
-        
+
         return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
                 | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
     }
@@ -1750,7 +1756,7 @@
         }
         return null;
     }
-    
+
     private void applyEnterAnimationLocked(WindowState win) {
         int transit = WindowManagerPolicy.TRANSIT_SHOW;
         if (win.mEnterAnimationPending) {
@@ -1768,7 +1774,7 @@
             // an animation of the same type, then just leave that one alone.
             return true;
         }
-        
+
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
@@ -1833,7 +1839,7 @@
         }
         return null;
     }
-    
+
     private boolean applyAnimationLocked(AppWindowToken wtoken,
             WindowManager.LayoutParams lp, int transit, boolean enter) {
         // Only apply an animation if the display isn't frozen.  If it is
@@ -1932,7 +1938,7 @@
         if (Binder.getCallingPid() == Process.myPid()) {
             return true;
         }
-        
+
         if (mContext.checkCallingPermission(permission)
                 == PackageManager.PERMISSION_GRANTED) {
             return true;
@@ -1944,7 +1950,7 @@
         Log.w(TAG, msg);
         return false;
     }
-    
+
     AppWindowToken findAppWindowToken(IBinder token) {
         WindowToken wtoken = mTokenMap.get(token);
         if (wtoken == null) {
@@ -1952,13 +1958,13 @@
         }
         return wtoken.appWindowToken;
     }
-    
+
     public void addWindowToken(IBinder token, int type) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addWindowToken()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        
+
         synchronized(mWindowMap) {
             WindowToken wtoken = mTokenMap.get(token);
             if (wtoken != null) {
@@ -1970,11 +1976,11 @@
             mTokenList.add(wtoken);
         }
     }
-    
+
     public void removeWindowToken(IBinder token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "removeWindowToken()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         final long origId = Binder.clearCallingIdentity();
@@ -1985,17 +1991,17 @@
                 boolean delayed = false;
                 if (!wtoken.hidden) {
                     wtoken.hidden = true;
-                    
+
                     final int N = wtoken.windows.size();
                     boolean changed = false;
-                    
+
                     for (int i=0; i<N; i++) {
                         WindowState win = wtoken.windows.get(i);
 
                         if (win.isAnimating()) {
                             delayed = true;
                         }
-                        
+
                         if (win.isVisibleNow()) {
                             applyAnimationLocked(win,
                                     WindowManagerPolicy.TRANSIT_EXIT, false);
@@ -2010,12 +2016,12 @@
                         performLayoutAndPlaceSurfacesLocked();
                         updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
                     }
-                    
+
                     if (delayed) {
                         mExitingTokens.add(wtoken);
                     }
                 }
-                
+
             } else {
                 Log.w(TAG, "Attempted to remove non-existing token: " + token);
             }
@@ -2027,9 +2033,9 @@
             int groupId, int requestedOrientation, boolean fullscreen) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        
+
         synchronized(mWindowMap) {
             AppWindowToken wtoken = findAppWindowToken(token.asBinder());
             if (wtoken != null) {
@@ -2044,19 +2050,19 @@
             if (localLOGV) Log.v(TAG, "Adding new app token: " + wtoken);
             mTokenMap.put(token.asBinder(), wtoken);
             mTokenList.add(wtoken);
-            
+
             // Application tokens start out hidden.
             wtoken.hidden = true;
             wtoken.hiddenRequested = true;
-            
+
             //dump();
         }
     }
-   
+
     public void setAppGroupId(IBinder token, int groupId) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppStartingIcon()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
@@ -2068,7 +2074,7 @@
             wtoken.groupId = groupId;
         }
     }
-    
+
     public int getOrientationFromWindowsLocked() {
         int pos = mWindows.size() - 1;
         while (pos >= 0) {
@@ -2092,7 +2098,7 @@
         }
         return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
-    
+
     public int getOrientationFromAppTokensLocked() {
             int pos = mAppTokens.size() - 1;
             int curGroup = 0;
@@ -2134,7 +2140,7 @@
                 // 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;
                 }
@@ -2151,11 +2157,25 @@
             }
             return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
-    
+
     public Configuration updateOrientationFromAppTokens(
             Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "updateOrientationFromAppTokens()")) {
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+        }
+        
         Configuration config;
         long ident = Binder.clearCallingIdentity();
+        config = updateOrientationFromAppTokensUnchecked(currentConfig,
+                freezeThisOneIfNeeded);
+        Binder.restoreCallingIdentity(ident);
+        return config;
+    }
+
+    Configuration updateOrientationFromAppTokensUnchecked(
+            Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
+        Configuration config;
         synchronized(mWindowMap) {
             config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded);
         }
@@ -2163,14 +2183,13 @@
             mLayoutNeeded = true;
             performLayoutAndPlaceSurfacesLocked();
         }
-        Binder.restoreCallingIdentity(ident);
         return config;
     }
-    
+
     /*
      * The orientation is computed from non-application windows first. If none of
      * the non-application windows specify orientation, the orientation is computed from
-     * application tokens. 
+     * application tokens.
      * @see android.view.IWindowManager#updateOrientationFromAppTokens(
      * android.os.IBinder)
      */
@@ -2180,7 +2199,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             int req = computeForcedAppOrientationLocked();
-            
+
             if (req != mForcedAppOrientation) {
                 changed = true;
                 mForcedAppOrientation = req;
@@ -2188,7 +2207,7 @@
                 //action like disabling/enabling sensors etc.,
                 mPolicy.setCurrentOrientationLw(req);
             }
-            
+
             if (changed) {
                 changed = setRotationUncheckedLocked(
                         WindowManagerPolicy.USE_LAST_ROTATION,
@@ -2220,10 +2239,10 @@
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        
+
         return null;
     }
-    
+
     int computeForcedAppOrientationLocked() {
         int req = getOrientationFromWindowsLocked();
         if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
@@ -2231,39 +2250,39 @@
         }
         return req;
     }
-    
+
     public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppOrientation()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        
+
         synchronized(mWindowMap) {
             AppWindowToken wtoken = findAppWindowToken(token.asBinder());
             if (wtoken == null) {
                 Log.w(TAG, "Attempted to set orientation of non-existing app token: " + token);
                 return;
             }
-            
+
             wtoken.requestedOrientation = requestedOrientation;
         }
     }
-    
+
     public int getAppOrientation(IApplicationToken token) {
         synchronized(mWindowMap) {
             AppWindowToken wtoken = findAppWindowToken(token.asBinder());
             if (wtoken == null) {
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             }
-            
+
             return wtoken.requestedOrientation;
         }
     }
-    
+
     public void setFocusedApp(IBinder token, boolean moveFocusNow) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setFocusedApp()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
@@ -2296,9 +2315,9 @@
     public void prepareAppTransition(int transit) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "prepareAppTransition()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        
+
         synchronized(mWindowMap) {
             if (DEBUG_APP_TRANSITIONS) Log.v(
                     TAG, "Prepare app transition: transit=" + transit
@@ -2306,6 +2325,14 @@
             if (!mDisplayFrozen) {
                 if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
                     mNextAppTransition = transit;
+                } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
+                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
+                    // Opening a new task always supersedes a close for the anim.
+                    mNextAppTransition = transit;
+                } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {
+                    // Opening a new activity always supersedes a close for the anim.
+                    mNextAppTransition = transit;
                 }
                 mAppTransitionReady = false;
                 mAppTransitionTimeout = false;
@@ -2321,13 +2348,13 @@
     public int getPendingAppTransition() {
         return mNextAppTransition;
     }
-    
+
     public void executeAppTransition() {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "executeAppTransition()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
-        
+
         synchronized(mWindowMap) {
             if (DEBUG_APP_TRANSITIONS) Log.v(
                     TAG, "Execute app transition: mNextAppTransition=" + mNextAppTransition);
@@ -2345,14 +2372,14 @@
             IBinder transferFrom, boolean createIfNeeded) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppStartingIcon()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
             if (DEBUG_STARTING_WINDOW) Log.v(
                     TAG, "setAppStartingIcon: token=" + token + " pkg=" + pkg
                     + " transferFrom=" + transferFrom);
-            
+
             AppWindowToken wtoken = findAppWindowToken(token);
             if (wtoken == null) {
                 Log.w(TAG, "Attempted to set icon of non-existing app token: " + token);
@@ -2365,11 +2392,11 @@
             if (mDisplayFrozen) {
                 return;
             }
-            
+
             if (wtoken.startingData != null) {
                 return;
             }
-            
+
             if (transferFrom != null) {
                 AppWindowToken ttoken = findAppWindowToken(transferFrom);
                 if (ttoken != null) {
@@ -2385,7 +2412,7 @@
                                 "Moving existing starting from " + ttoken
                                 + " to " + wtoken);
                         final long origId = Binder.clearCallingIdentity();
-                        
+
                         // Transfer the starting window over to the new
                         // token.
                         wtoken.startingData = ttoken.startingData;
@@ -2403,7 +2430,7 @@
                         ttoken.allAppWindows.remove(startingWindow);
                         addWindowToListInOrderLocked(startingWindow, true);
                         wtoken.allAppWindows.add(startingWindow);
-                        
+
                         // Propagate other interesting state between the
                         // tokens.  If the old token is displayed, we should
                         // immediately force the new one to be displayed.  If
@@ -2433,7 +2460,7 @@
                             wtoken.updateLayers();
                             ttoken.updateLayers();
                         }
-                        
+
                         updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
                         mLayoutNeeded = true;
                         performLayoutAndPlaceSurfacesLocked();
@@ -2463,7 +2490,7 @@
             if (!createIfNeeded) {
                 return;
             }
-            
+
             mStartingIconInTransition = true;
             wtoken.startingData = new StartingData(
                     pkg, theme, nonLocalizedLabel,
@@ -2479,7 +2506,7 @@
     public void setAppWillBeHidden(IBinder token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppWillBeHidden()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         AppWindowToken wtoken;
@@ -2493,7 +2520,7 @@
             wtoken.willBeHidden = true;
         }
     }
-    
+
     boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
             boolean visible, int transit, boolean performLayout) {
         boolean delayed = false;
@@ -2502,7 +2529,7 @@
             wtoken.clientHidden = !visible;
             wtoken.sendAppVisibilityToClients();
         }
-        
+
         wtoken.willBeHidden = false;
         if (wtoken.hidden == visible) {
             final int N = wtoken.allAppWindows.size();
@@ -2510,9 +2537,9 @@
             if (DEBUG_APP_TRANSITIONS) Log.v(
                 TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
                 + " performLayout=" + performLayout);
-            
+
             boolean runningAppAnimation = false;
-            
+
             if (transit != WindowManagerPolicy.TRANSIT_NONE) {
                 if (wtoken.animation == sDummyAnimation) {
                     wtoken.animation = null;
@@ -2523,7 +2550,7 @@
                     delayed = runningAppAnimation = true;
                 }
             }
-            
+
             for (int i=0; i<N; i++) {
                 WindowState win = wtoken.allAppWindows.get(i);
                 if (win == wtoken.startingWindow) {
@@ -2533,7 +2560,7 @@
                 if (win.isAnimating()) {
                     delayed = true;
                 }
-                
+
                 //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible());
                 //win.dump("  ");
                 if (visible) {
@@ -2568,11 +2595,11 @@
                     swin.mPolicyVisibilityAfterAnim = false;
                  }
             }
-            
+
             if (DEBUG_APP_TRANSITIONS) Log.v(TAG, "setTokenVisibilityLocked: " + wtoken
                       + ": hidden=" + wtoken.hidden + " hiddenRequested="
                       + wtoken.hiddenRequested);
-            
+
             if (changed && performLayout) {
                 mLayoutNeeded = true;
                 updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
@@ -2583,14 +2610,14 @@
         if (wtoken.animation != null) {
             delayed = true;
         }
-        
+
         return delayed;
     }
 
     public void setAppVisibility(IBinder token, boolean visible) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppVisibility()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         AppWindowToken wtoken;
@@ -2610,7 +2637,7 @@
                         + " hidden=" + wtoken.hidden
                         + " hiddenRequested=" + wtoken.hiddenRequested, e);
             }
-            
+
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
             if (!mDisplayFrozen && mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
@@ -2619,7 +2646,7 @@
                     return;
                 }
                 wtoken.hiddenRequested = !visible;
-                
+
                 if (DEBUG_APP_TRANSITIONS) Log.v(
                         TAG, "Setting dummy animation on: " + wtoken);
                 wtoken.setDummyAnimation();
@@ -2631,7 +2658,7 @@
                     wtoken.allDrawn = false;
                     wtoken.startingDisplayed = false;
                     wtoken.startingMoved = false;
-                    
+
                     if (wtoken.clientHidden) {
                         // In the case where we are making an app visible
                         // but holding off for a transition, we still need
@@ -2647,7 +2674,7 @@
                 }
                 return;
             }
-            
+
             final long origId = Binder.clearCallingIdentity();
             setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_NONE, true);
             wtoken.updateReportedVisibilityLocked();
@@ -2688,7 +2715,7 @@
             }
         }
     }
-    
+
     public void startAppFreezingScreenLocked(AppWindowToken wtoken,
             int configChanges) {
         if (DEBUG_ORIENTATION) {
@@ -2716,11 +2743,11 @@
             }
         }
     }
-    
+
     public void startAppFreezingScreen(IBinder token, int configChanges) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppFreezingScreen()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
@@ -2728,7 +2755,7 @@
                 if (DEBUG_ORIENTATION) Log.v(TAG, "Skipping set freeze of " + token);
                 return;
             }
-            
+
             AppWindowToken wtoken = findAppWindowToken(token);
             if (wtoken == null || wtoken.appToken == null) {
                 Log.w(TAG, "Attempted to freeze screen with non-existing app token: " + wtoken);
@@ -2739,11 +2766,11 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
-    
+
     public void stopAppFreezingScreen(IBinder token, boolean force) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppFreezingScreen()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
@@ -2758,11 +2785,11 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
-    
+
     public void removeAppToken(IBinder token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "removeAppToken()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         AppWindowToken wtoken = null;
@@ -2807,7 +2834,7 @@
             } else {
                 Log.w(TAG, "Attempted to remove non-existing app token: " + token);
             }
-            
+
             if (!delayed && wtoken != null) {
                 wtoken.updateReportedVisibilityLocked();
             }
@@ -2841,13 +2868,13 @@
             Log.v(TAG, "  #" + i + ": " + mAppTokens.get(i).token);
         }
     }
-    
+
     void dumpWindowsLocked() {
         for (int i=mWindows.size()-1; i>=0; i--) {
             Log.v(TAG, "  #" + i + ": " + mWindows.get(i));
         }
     }
-    
+
     private int findWindowOffsetLocked(int tokenPos) {
         final int NW = mWindows.size();
 
@@ -2918,7 +2945,7 @@
         }
         return index;
     }
-    
+
     private final int reAddAppWindowsLocked(int index, WindowToken token) {
         final int NW = token.windows.size();
         for (int i=0; i<NW; i++) {
@@ -2930,7 +2957,7 @@
     public void moveAppToken(int index, IBinder token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "moveAppToken()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized(mWindowMap) {
@@ -2945,7 +2972,7 @@
             mAppTokens.add(index, wtoken);
             if (DEBUG_REORDER) Log.v(TAG, "Moved " + token + " to " + index + ":");
             if (DEBUG_REORDER) dumpAppTokensLocked();
-            
+
             final long origId = Binder.clearCallingIdentity();
             if (DEBUG_REORDER) Log.v(TAG, "Removing windows in " + token + ":");
             if (DEBUG_REORDER) dumpWindowsLocked();
@@ -3012,7 +3039,7 @@
     public void moveAppTokensToTop(List<IBinder> tokens) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "moveAppTokensToTop()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         final long origId = Binder.clearCallingIdentity();
@@ -3033,7 +3060,7 @@
     public void moveAppTokensToBottom(List<IBinder> tokens) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "moveAppTokensToBottom()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         final long origId = Binder.clearCallingIdentity();
@@ -3056,7 +3083,7 @@
     // -------------------------------------------------------------
     // Misc IWindowSession methods
     // -------------------------------------------------------------
-    
+
     public void disableKeyguard(IBinder token, String tag) {
         if (mContext.checkCallingPermission(android.Manifest.permission.DISABLE_KEYGUARD)
             != PackageManager.PERMISSION_GRANTED) {
@@ -3110,17 +3137,17 @@
     public boolean inKeyguardRestrictedInputMode() {
         return mPolicy.inKeyguardRestrictedKeyInputMode();
     }
-    
+
     static float fixScale(float scale) {
         if (scale < 0) scale = 0;
         else if (scale > 20) scale = 20;
         return Math.abs(scale);
     }
-    
+
     public void setAnimationScale(int which, float scale) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
                 "setAnimationScale()")) {
-            return;
+            throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
         }
 
         if (scale < 0) scale = 0;
@@ -3130,15 +3157,15 @@
             case 0: mWindowAnimationScale = fixScale(scale); break;
             case 1: mTransitionAnimationScale = fixScale(scale); break;
         }
-        
+
         // Persist setting
         mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
     }
-    
+
     public void setAnimationScales(float[] scales) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
                 "setAnimationScale()")) {
-            return;
+            throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
         }
 
         if (scales != null) {
@@ -3149,11 +3176,11 @@
                 mTransitionAnimationScale = fixScale(scales[1]);
             }
         }
-        
+
         // Persist setting
         mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
     }
-    
+
     public float getAnimationScale(int which) {
         switch (which) {
             case 0: return mWindowAnimationScale;
@@ -3161,63 +3188,63 @@
         }
         return 0;
     }
-    
+
     public float[] getAnimationScales() {
         return new float[] { mWindowAnimationScale, mTransitionAnimationScale };
     }
-    
+
     public int getSwitchState(int sw) {
         if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
                 "getSwitchState()")) {
-            return -1;
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
         return KeyInputQueue.getSwitchState(sw);
     }
-    
+
     public int getSwitchStateForDevice(int devid, int sw) {
         if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
                 "getSwitchStateForDevice()")) {
-            return -1;
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
         return KeyInputQueue.getSwitchState(devid, sw);
     }
-    
+
     public int getScancodeState(int sw) {
         if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
                 "getScancodeState()")) {
-            return -1;
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
         return KeyInputQueue.getScancodeState(sw);
     }
-    
+
     public int getScancodeStateForDevice(int devid, int sw) {
         if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
                 "getScancodeStateForDevice()")) {
-            return -1;
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
         return KeyInputQueue.getScancodeState(devid, sw);
     }
-    
+
     public int getKeycodeState(int sw) {
         if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
                 "getKeycodeState()")) {
-            return -1;
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
         return KeyInputQueue.getKeycodeState(sw);
     }
-    
+
     public int getKeycodeStateForDevice(int devid, int sw) {
         if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
                 "getKeycodeStateForDevice()")) {
-            return -1;
+            throw new SecurityException("Requires READ_INPUT_STATE permission");
         }
         return KeyInputQueue.getKeycodeState(devid, sw);
     }
-    
+
     public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
         return KeyInputQueue.hasKeys(keycodes, keyExists);
     }
-    
+
     public void enableScreenAfterBoot() {
         synchronized(mWindowMap) {
             if (mSystemBooted) {
@@ -3225,10 +3252,10 @@
             }
             mSystemBooted = true;
         }
-        
+
         performEnableScreen();
     }
-    
+
     public void enableScreenIfNeededLocked() {
         if (mDisplayEnabled) {
             return;
@@ -3238,7 +3265,7 @@
         }
         mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN));
     }
-    
+
     public void performEnableScreen() {
         synchronized(mWindowMap) {
             if (mDisplayEnabled) {
@@ -3247,7 +3274,7 @@
             if (!mSystemBooted) {
                 return;
             }
-            
+
             // Don't enable the screen until all existing windows
             // have been drawn.
             final int N = mWindows.size();
@@ -3257,7 +3284,7 @@
                     return;
                 }
             }
-            
+
             mDisplayEnabled = true;
             if (false) {
                 Log.i(TAG, "ENABLING SCREEN!");
@@ -3280,41 +3307,41 @@
                 Log.e(TAG, "Boot completed: SurfaceFlinger is dead!");
             }
         }
-        
+
         mPolicy.enableScreenAfterBoot();
-        
+
         // Make sure the last requested orientation has been applied.
         setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false,
                 mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
     }
-    
+
     public void setInTouchMode(boolean mode) {
         synchronized(mWindowMap) {
             mInTouchMode = mode;
         }
     }
 
-    public void setRotation(int rotation, 
+    public void setRotation(int rotation,
             boolean alwaysSendConfiguration, int animFlags) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                 "setRotation()")) {
-            return;
+            throw new SecurityException("Requires SET_ORIENTATION permission");
         }
 
         setRotationUnchecked(rotation, alwaysSendConfiguration, animFlags);
     }
-    
+
     public void setRotationUnchecked(int rotation,
             boolean alwaysSendConfiguration, int animFlags) {
         if(DEBUG_ORIENTATION) Log.v(TAG,
                 "alwaysSendConfiguration set to "+alwaysSendConfiguration);
-        
+
         long origId = Binder.clearCallingIdentity();
         boolean changed;
         synchronized(mWindowMap) {
             changed = setRotationUncheckedLocked(rotation, animFlags);
         }
-        
+
         if (changed) {
             sendNewConfiguration();
             synchronized(mWindowMap) {
@@ -3325,10 +3352,10 @@
             //update configuration ignoring orientation change
             sendNewConfiguration();
         }
-        
+
         Binder.restoreCallingIdentity(origId);
     }
-    
+
     public boolean setRotationUncheckedLocked(int rotation, int animFlags) {
         boolean changed;
         if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) {
@@ -3342,9 +3369,9 @@
                 mRotation, mDisplayEnabled);
         if (DEBUG_ORIENTATION) Log.v(TAG, "new rotation is set to " + rotation);
         changed = mDisplayEnabled && mRotation != rotation;
-        
+
         if (changed) {
-            if (DEBUG_ORIENTATION) Log.v(TAG, 
+            if (DEBUG_ORIENTATION) Log.v(TAG,
                     "Rotation changed to " + rotation
                     + " from " + mRotation
                     + " (forceApp=" + mForcedAppOrientation
@@ -3373,10 +3400,10 @@
                 }
             }
         } //end if changed
-        
+
         return changed;
     }
-    
+
     public int getRotation() {
         return mRotation;
     }
@@ -3388,14 +3415,17 @@
                 synchronized (mWindowMap) {
                     for (int i=0; i<mRotationWatchers.size(); i++) {
                         if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
-                            mRotationWatchers.remove(i);
+                            IRotationWatcher removed = mRotationWatchers.remove(i);
+                            if (removed != null) {
+                                removed.asBinder().unlinkToDeath(this, 0);
+                            }
                             i--;
                         }
                     }
                 }
             }
         };
-        
+
         synchronized (mWindowMap) {
             try {
                 watcher.asBinder().linkToDeath(dr, 0);
@@ -3403,7 +3433,7 @@
             } catch (RemoteException e) {
                 // Client died, no cleanup needed.
             }
-            
+
             return mRotation;
         }
     }
@@ -3419,7 +3449,7 @@
      * @see com.android.server.ViewServer#VIEW_SERVER_DEFAULT_PORT
      */
     public boolean startViewServer(int port) {
-        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+        if (isSystemSecure()) {
             return false;
         }
 
@@ -3436,7 +3466,7 @@
                 try {
                     return mViewServer.start();
                 } catch (IOException e) {
-                    Log.w(TAG, "View server did not start");                    
+                    Log.w(TAG, "View server did not start");
                 }
             }
             return false;
@@ -3451,6 +3481,11 @@
         return false;
     }
 
+    private boolean isSystemSecure() {
+        return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) &&
+                "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+    }
+
     /**
      * Stops the view server if it exists.
      *
@@ -3460,7 +3495,7 @@
      * @see com.android.server.ViewServer
      */
     public boolean stopViewServer() {
-        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+        if (isSystemSecure()) {
             return false;
         }
 
@@ -3482,7 +3517,7 @@
      * @see com.android.server.ViewServer
      */
     public boolean isViewServerRunning() {
-        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+        if (isSystemSecure()) {
             return false;
         }
 
@@ -3503,7 +3538,7 @@
      * @return False if an error occured, true otherwise.
      */
     boolean viewServerListWindows(Socket client) {
-        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+        if (isSystemSecure()) {
             return false;
         }
 
@@ -3570,7 +3605,7 @@
      *         not indicate whether the command itself was successful.
      */
     boolean viewServerWindowCommand(Socket client, String command, String parameters) {
-        if ("1".equals(SystemProperties.get(SYSTEM_SECURE, "0"))) {
+        if (isSystemSecure()) {
             return false;
         }
 
@@ -3660,13 +3695,13 @@
         } catch (RemoteException e) {
         }
     }
-    
+
     public Configuration computeNewConfiguration() {
         synchronized (mWindowMap) {
             return computeNewConfigurationLocked();
         }
     }
-    
+
     Configuration computeNewConfigurationLocked() {
         Configuration config = new Configuration();
         if (!computeNewConfigurationLocked(config)) {
@@ -3687,7 +3722,7 @@
         }
         return config;
     }
-    
+
     boolean computeNewConfigurationLocked(Configuration config) {
         if (mDisplay == null) {
             return false;
@@ -3702,12 +3737,46 @@
             orientation = Configuration.ORIENTATION_LANDSCAPE;
         }
         config.orientation = orientation;
+        
+        if (screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+            // Note we only do this once because at this point we don't
+            // expect the screen to change in this way at runtime, and want
+            // to avoid all of this computation for every config change.
+            DisplayMetrics dm = new DisplayMetrics();
+            mDisplay.getMetrics(dm);
+            int longSize = dw;
+            int shortSize = dh;
+            if (longSize < shortSize) {
+                int tmp = longSize;
+                longSize = shortSize;
+                shortSize = tmp;
+            }
+            longSize = (int)(longSize/dm.density);
+            shortSize = (int)(shortSize/dm.density);
+            
+            // These semi-magic numbers define our compatibility modes for
+            // applications with different screens.  Don't change unless you
+            // make sure to test lots and lots of apps!
+            if (longSize < 470) {
+                // This is shorter than an HVGA normal density screen (which
+                // is 480 pixels on its long side).
+                screenLayout = Configuration.SCREENLAYOUT_SMALL;
+            } else if (longSize > 490 && shortSize > 330) {
+                // This is larger than an HVGA normal density screen (which
+                // is 480x320 pixels).
+                screenLayout = Configuration.SCREENLAYOUT_LARGE;
+            } else {
+                screenLayout = Configuration.SCREENLAYOUT_NORMAL;
+            }
+        }
+        config.screenLayout = screenLayout;
+        
         config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
         config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
         mPolicy.adjustConfigurationLw(config);
         return true;
     }
-    
+
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
@@ -3771,15 +3840,15 @@
     /**
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
-    private boolean dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
+    private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
         if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG,
                 "dispatchPointer " + ev);
 
         Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
-                ev, true, false);
-        
+                ev, true, false, pid, uid);
+
         int action = ev.getAction();
-        
+
         if (action == MotionEvent.ACTION_UP) {
             // let go of our target
             mKeyWaiter.mMotionTarget = null;
@@ -3801,20 +3870,20 @@
                 mQueue.recycleEvent(qev);
             }
             ev.recycle();
-            return false;
+            return INJECT_FAILED;
         }
         if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
             if (qev != null) {
                 mQueue.recycleEvent(qev);
             }
             ev.recycle();
-            return true;
+            return INJECT_SUCCEEDED;
         }
-        
+
         WindowState target = (WindowState)targetObj;
-        
+
         final long eventTime = ev.getEventTime();
-        
+
         //Log.i(TAG, "Sending " + ev + " to " + target);
 
         if (uid != 0 && uid != target.mSession.mUid) {
@@ -3828,11 +3897,11 @@
                     mQueue.recycleEvent(qev);
                 }
                 ev.recycle();
-                return false;
+                return INJECT_NO_PERMISSION;
             }
         }
-        
-        if ((target.mAttrs.flags & 
+
+        if ((target.mAttrs.flags &
                         WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
             //target wants to ignore fat touch events
             boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev);
@@ -3859,7 +3928,7 @@
                     if(mFatTouch) {
                         //two cases here
                         //an invalid down followed by 0 or moves(valid or invalid)
-                        //a valid down,  invalid move, more moves. want to ignore till up 
+                        //a valid down,  invalid move, more moves. want to ignore till up
                         returnFlag = true;
                     } else if(cheekPress) {
                         //valid down followed by invalid moves
@@ -3878,7 +3947,7 @@
                     mQueue.recycleEvent(qev);
                 }
                 ev.recycle();
-                return false;
+                return INJECT_FAILED;
             }
         } //end if target
 
@@ -3944,7 +4013,7 @@
                 mKeyWaiter.bindTargetWindowLocked(target);
             }
         }
-        
+
         // finally offset the event to the target's coordinate system and
         // dispatch the event.
         try {
@@ -3952,7 +4021,7 @@
                 Log.v(TAG, "Delivering pointer " + qev + " to " + target);
             }
             target.mClient.dispatchPointer(ev, eventTime);
-            return true;
+            return INJECT_SUCCEEDED;
         } catch (android.os.RemoteException e) {
             Log.i(TAG, "WINDOW DIED during motion dispatch: " + target);
             mKeyWaiter.mMotionTarget = null;
@@ -3963,36 +4032,36 @@
                 // removed.
             }
         }
-        return false;
+        return INJECT_FAILED;
     }
-    
+
     /**
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
-    private boolean dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
+    private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
         if (DEBUG_INPUT) Log.v(
                 TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
-        
+
         Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
-                ev, false, false);
+                ev, false, false, pid, uid);
         if (focusObj == null) {
             Log.w(TAG, "No focus window, dropping trackball: " + ev);
             if (qev != null) {
                 mQueue.recycleEvent(qev);
             }
             ev.recycle();
-            return false;
+            return INJECT_FAILED;
         }
         if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
             if (qev != null) {
                 mQueue.recycleEvent(qev);
             }
             ev.recycle();
-            return true;
+            return INJECT_SUCCEEDED;
         }
-        
+
         WindowState focus = (WindowState)focusObj;
-        
+
         if (uid != 0 && uid != focus.mSession.mUid) {
             if (mContext.checkPermission(
                     android.Manifest.permission.INJECT_EVENTS, pid, uid)
@@ -4004,12 +4073,12 @@
                     mQueue.recycleEvent(qev);
                 }
                 ev.recycle();
-                return false;
+                return INJECT_NO_PERMISSION;
             }
         }
-        
+
         final long eventTime = ev.getEventTime();
-        
+
         synchronized(mWindowMap) {
             if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) {
                 mKeyWaiter.bindTargetWindowLocked(focus,
@@ -4021,10 +4090,10 @@
                 mKeyWaiter.bindTargetWindowLocked(focus);
             }
         }
-        
+
         try {
             focus.mClient.dispatchTrackball(ev, eventTime);
-            return true;
+            return INJECT_SUCCEEDED;
         } catch (android.os.RemoteException e) {
             Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
             try {
@@ -4034,28 +4103,28 @@
                 // removed.
             }
         }
-        
-        return false;
+
+        return INJECT_FAILED;
     }
-    
+
     /**
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
-    private boolean dispatchKey(KeyEvent event, int pid, int uid) {
+    private int dispatchKey(KeyEvent event, int pid, int uid) {
         if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
 
         Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
-                null, false, false);
+                null, false, false, pid, uid);
         if (focusObj == null) {
             Log.w(TAG, "No focus window, dropping: " + event);
-            return false;
+            return INJECT_FAILED;
         }
         if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) {
-            return true;
+            return INJECT_SUCCEEDED;
         }
-        
+
         WindowState focus = (WindowState)focusObj;
-        
+
         if (DEBUG_INPUT) Log.v(
             TAG, "Dispatching to " + focus + ": " + event);
 
@@ -4066,10 +4135,10 @@
                 Log.w(TAG, "Permission denied: injecting key event from pid "
                         + pid + " uid " + uid + " to window " + focus
                         + " owned by uid " + focus.mSession.mUid);
-                return false;
+                return INJECT_NO_PERMISSION;
             }
         }
-        
+
         synchronized(mWindowMap) {
             mKeyWaiter.bindTargetWindowLocked(focus);
         }
@@ -4077,14 +4146,14 @@
         // NOSHIP extra state logging
         mKeyWaiter.recordDispatchState(event, focus);
         // END NOSHIP
-        
+
         try {
             if (DEBUG_INPUT || DEBUG_FOCUS) {
                 Log.v(TAG, "Delivering key " + event.getKeyCode()
                         + " to " + focus);
             }
             focus.mClient.dispatchKey(event);
-            return true;
+            return INJECT_SUCCEEDED;
         } catch (android.os.RemoteException e) {
             Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
             try {
@@ -4094,14 +4163,14 @@
                 // removed.
             }
         }
-        
-        return false;
+
+        return INJECT_FAILED;
     }
-    
+
     public void pauseKeyDispatching(IBinder _token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "pauseKeyDispatching()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized (mWindowMap) {
@@ -4115,7 +4184,7 @@
     public void resumeKeyDispatching(IBinder _token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "resumeKeyDispatching()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized (mWindowMap) {
@@ -4129,18 +4198,18 @@
     public void setEventDispatching(boolean enabled) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "resumeKeyDispatching()")) {
-            return;
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
         }
 
         synchronized (mWindowMap) {
             mKeyWaiter.setEventDispatchingLocked(enabled);
         }
     }
-    
+
     /**
      * Injects a keystroke event into the UI.
-     * 
-     * @param ev A motion event describing the keystroke action.  (Be sure to use 
+     *
+     * @param ev A motion event describing the keystroke action.  (Be sure to use
      * {@link SystemClock#uptimeMillis()} as the timebase.)
      * @param sync If true, wait for the event to be completed before returning to the caller.
      * @return Returns true if event was dispatched, false if it was dropped for any reason
@@ -4162,47 +4231,80 @@
         KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
                 deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
 
-        boolean result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchKey(newEvent, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
-        return result;
+        Binder.restoreCallingIdentity(ident);
+        switch (result) {
+            case INJECT_NO_PERMISSION:
+                throw new SecurityException(
+                        "Injecting to another application requires INJECT_EVENT permission");
+            case INJECT_SUCCEEDED:
+                return true;
+        }
+        return false;
     }
 
     /**
      * Inject a pointer (touch) event into the UI.
-     * 
-     * @param ev A motion event describing the pointer (touch) action.  (As noted in 
-     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 
+     *
+     * @param ev A motion event describing the pointer (touch) action.  (As noted in
+     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
      * {@link SystemClock#uptimeMillis()} as the timebase.)
      * @param sync If true, wait for the event to be completed before returning to the caller.
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
     public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
-        boolean result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchPointer(null, ev, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
-        return result;        
+        Binder.restoreCallingIdentity(ident);
+        switch (result) {
+            case INJECT_NO_PERMISSION:
+                throw new SecurityException(
+                        "Injecting to another application requires INJECT_EVENT permission");
+            case INJECT_SUCCEEDED:
+                return true;
+        }
+        return false;
     }
-    
+
     /**
      * Inject a trackball (navigation device) event into the UI.
-     * 
-     * @param ev A motion event describing the trackball action.  (As noted in 
-     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 
+     *
+     * @param ev A motion event describing the trackball action.  (As noted in
+     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
      * {@link SystemClock#uptimeMillis()} as the timebase.)
      * @param sync If true, wait for the event to be completed before returning to the caller.
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
     public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
-        boolean result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchTrackball(null, ev, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
-        return result;
+        Binder.restoreCallingIdentity(ident);
+        switch (result) {
+            case INJECT_NO_PERMISSION:
+                throw new SecurityException(
+                        "Injecting to another application requires INJECT_EVENT permission");
+            case INJECT_SUCCEEDED:
+                return true;
+        }
+        return false;
     }
-    
+
     private WindowState getFocusedWindow() {
         synchronized (mWindowMap) {
             return getFocusedWindowLocked();
@@ -4212,7 +4314,7 @@
     private WindowState getFocusedWindowLocked() {
         return mCurrentFocus;
     }
-    
+
     /**
      * This class holds the state for dispatching key events.  This state
      * is protected by the KeyWaiter instance, NOT by the window lock.  You
@@ -4234,7 +4336,7 @@
             private boolean wasFrozen;
             private boolean focusPaused;
             private WindowState curFocus;
-            
+
             DispatchState(KeyEvent theEvent, WindowState theFocus) {
                 focus = theFocus;
                 event = theEvent;
@@ -4256,7 +4358,7 @@
                     focusPaused = theFocus.mToken.paused;
                 }
             }
-            
+
             public String toString() {
                 return "{{" + event + " to " + focus + " @ " + time
                         + " lw=" + lastWin + " lb=" + lastBinder
@@ -4275,10 +4377,10 @@
         public static final int RETURN_NOTHING = 0;
         public static final int RETURN_PENDING_POINTER = 1;
         public static final int RETURN_PENDING_TRACKBALL = 2;
-        
+
         final Object SKIP_TARGET_TOKEN = new Object();
         final Object CONSUMED_EVENT_TOKEN = new Object();
-        
+
         private WindowState mLastWin = null;
         private IBinder mLastBinder = null;
         private boolean mFinished = true;
@@ -4286,10 +4388,10 @@
         private boolean mEventDispatching = true;
         private long mTimeToSwitch = 0;
         /* package */ boolean mWasFrozen = false;
-        
+
         // Target of Motion events
         WindowState mMotionTarget;
-        
+
         // Windows above the target who would like to receive an "outside"
         // touch event for any down events outside of them.
         WindowState mOutsideTouchTargets;
@@ -4301,7 +4403,7 @@
          */
         Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
                 MotionEvent nextMotion, boolean isPointerEvent,
-                boolean failIfTimeout) {
+                boolean failIfTimeout, int callingPid, int callingUid) {
             long startTime = SystemClock.uptimeMillis();
             long keyDispatchingTimeout = 5 * 1000;
             long waitedFor = 0;
@@ -4319,7 +4421,7 @@
                         ", mLastWin=" + mLastWin);
                 if (targetIsNew) {
                     Object target = findTargetWindow(nextKey, qev, nextMotion,
-                            isPointerEvent);
+                            isPointerEvent, callingPid, callingUid);
                     if (target == SKIP_TARGET_TOKEN) {
                         // The user has pressed a special key, and we are
                         // dropping all pending events before it.
@@ -4334,9 +4436,9 @@
                     }
                     targetWin = (WindowState)target;
                 }
-                
+
                 AppWindowToken targetApp = null;
-                
+
                 // Now: is it okay to send the next event to this window?
                 synchronized (this) {
                     // First: did we come here based on the last window not
@@ -4345,7 +4447,7 @@
                     if (!targetIsNew && mLastWin == null) {
                         continue;
                     }
-                    
+
                     // We never dispatch events if not finished with the
                     // last one, or the display is frozen.
                     if (mFinished && !mDisplayFrozen) {
@@ -4364,7 +4466,7 @@
                             if (targetIsNew && !targetWin.mToken.paused) {
                                 return targetWin;
                             }
-                        
+
                         // If we didn't find a target window, and there is no
                         // focused app window, then just eat the events.
                         } else if (mFocusedApp == null) {
@@ -4374,7 +4476,7 @@
                             return null;
                         }
                     }
-                    
+
                     if (DEBUG_INPUT) Log.v(
                             TAG, "Waiting for last key in " + mLastBinder
                             + " target=" + targetWin
@@ -4385,10 +4487,10 @@
                             + (targetWin != null ? targetWin.mToken.paused : false)
                             + " mFocusedApp=" + mFocusedApp
                             + " mCurrentFocus=" + mCurrentFocus);
-                    
+
                     targetApp = targetWin != null
                             ? targetWin.mAppToken : mFocusedApp;
-                    
+
                     long curTimeout = keyDispatchingTimeout;
                     if (mTimeToSwitch != 0) {
                         long now = SystemClock.uptimeMillis();
@@ -4404,7 +4506,7 @@
                             curTimeout = switchTimeout;
                         }
                     }
-                    
+
                     try {
                         // after that continue
                         // processing keys, so we don't get stuck.
@@ -4468,7 +4570,7 @@
                     synchronized (this) {
                         if (abort && (mLastWin == targetWin || targetWin == null)) {
                             mFinished = true;
-                            if (mLastWin != null) { 
+                            if (mLastWin != null) {
                                 if (DEBUG_INPUT) Log.v(TAG,
                                         "Window " + mLastWin +
                                         " timed out on key input");
@@ -4493,41 +4595,56 @@
                 }
             }
         }
-        
+
         Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
-                MotionEvent nextMotion, boolean isPointerEvent) {
+                MotionEvent nextMotion, boolean isPointerEvent,
+                int callingPid, int callingUid) {
             mOutsideTouchTargets = null;
-            
+
             if (nextKey != null) {
                 // Find the target window for a normal key event.
                 final int keycode = nextKey.getKeyCode();
                 final int repeatCount = nextKey.getRepeatCount();
                 final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
                 boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+                
                 if (!dispatch) {
-                    mPolicy.interceptKeyTi(null, keycode,
-                            nextKey.getMetaState(), down, repeatCount);
+                    if (callingUid == 0 ||
+                            mContext.checkPermission(
+                                    android.Manifest.permission.INJECT_EVENTS,
+                                    callingPid, callingUid)
+                                    == PackageManager.PERMISSION_GRANTED) {
+                        mPolicy.interceptKeyTi(null, keycode,
+                                nextKey.getMetaState(), down, repeatCount);
+                    }
                     Log.w(TAG, "Event timeout during app switch: dropping "
                             + nextKey);
                     return SKIP_TARGET_TOKEN;
                 }
-                
+
                 // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")");
-                
+
                 WindowState focus = null;
                 synchronized(mWindowMap) {
                     focus = getFocusedWindowLocked();
                 }
-                
+
                 wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-                
-                if (mPolicy.interceptKeyTi(focus,
-                        keycode, nextKey.getMetaState(), down, repeatCount)) {
-                    return CONSUMED_EVENT_TOKEN;
+
+                if (callingUid == 0 ||
+                        (focus != null && callingUid == focus.mSession.mUid) ||
+                        mContext.checkPermission(
+                                android.Manifest.permission.INJECT_EVENTS,
+                                callingPid, callingUid)
+                                == PackageManager.PERMISSION_GRANTED) {
+                    if (mPolicy.interceptKeyTi(focus,
+                            keycode, nextKey.getMetaState(), down, repeatCount)) {
+                        return CONSUMED_EVENT_TOKEN;
+                    }
                 }
-                
+
                 return focus;
-                
+
             } else if (!isPointerEvent) {
                 boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1);
                 if (!dispatch) {
@@ -4535,20 +4652,20 @@
                             + nextMotion);
                     return SKIP_TARGET_TOKEN;
                 }
-                
+
                 WindowState focus = null;
                 synchronized(mWindowMap) {
                     focus = getFocusedWindowLocked();
                 }
-                
+
                 wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
                 return focus;
             }
-            
+
             if (nextMotion == null) {
                 return SKIP_TARGET_TOKEN;
             }
-            
+
             boolean dispatch = mKeyWaiter.checkShouldDispatchKey(
                     KeyEvent.KEYCODE_UNKNOWN);
             if (!dispatch) {
@@ -4556,18 +4673,18 @@
                         + nextMotion);
                 return SKIP_TARGET_TOKEN;
             }
-            
+
             // Find the target window for a pointer event.
             int action = nextMotion.getAction();
             final float xf = nextMotion.getX();
             final float yf = nextMotion.getY();
             final long eventTime = nextMotion.getEventTime();
-            
+
             final boolean screenWasOff = qev != null
                     && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-            
+
             WindowState target = null;
-            
+
             synchronized(mWindowMap) {
                 synchronized (this) {
                     if (action == MotionEvent.ACTION_DOWN) {
@@ -4580,12 +4697,12 @@
                                     + mMotionTarget);
                             mMotionTarget = null;
                         }
-                        
+
                         // ACTION_DOWN is special, because we need to lock next events to
                         // the window we'll land onto.
                         final int x = (int)xf;
                         final int y = (int)yf;
-    
+
                         final ArrayList windows = mWindows;
                         final int N = windows.size();
                         WindowState topErrWindow = null;
@@ -4646,7 +4763,7 @@
                                 }
                                 break;
                             }
-                            
+
                             if ((flags & WindowManager.LayoutParams
                                     .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
                                 child.mNextOutsideTouch = mOutsideTouchTargets;
@@ -4663,18 +4780,18 @@
                             mMotionTarget = null;
                         }
                     }
-                    
+
                     target = mMotionTarget;
                 }
             }
-            
+
             wakeupIfNeeded(target, eventType(nextMotion));
-            
+
             // Pointer events are a little different -- if there isn't a
             // target found for any event, then just drop it.
             return target != null ? target : SKIP_TARGET_TOKEN;
         }
-        
+
         boolean checkShouldDispatchKey(int keycode) {
             synchronized (this) {
                 if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) {
@@ -4688,14 +4805,14 @@
                 return true;
             }
         }
-        
+
         void bindTargetWindowLocked(WindowState win,
                 int pendingWhat, QueuedEvent pendingMotion) {
             synchronized (this) {
                 bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion);
             }
         }
-        
+
         void bindTargetWindowLocked(WindowState win) {
             synchronized (this) {
                 bindTargetWindowLockedLocked(win, RETURN_NOTHING, null);
@@ -4713,7 +4830,7 @@
                     releasePendingPointerLocked(s);
                     s.mPendingPointerMove = pendingMotion;
                     s.mPendingPointerWindow = win;
-                    if (DEBUG_INPUT) Log.v(TAG, 
+                    if (DEBUG_INPUT) Log.v(TAG,
                             "bindTargetToWindow " + s.mPendingPointerMove);
                 } else if (pendingWhat == RETURN_PENDING_TRACKBALL) {
                     releasePendingTrackballLocked(s);
@@ -4722,7 +4839,7 @@
                 }
             }
         }
-        
+
         void releasePendingPointerLocked(Session s) {
             if (DEBUG_INPUT) Log.v(TAG,
                     "releasePendingPointer " + s.mPendingPointerMove);
@@ -4731,14 +4848,14 @@
                 s.mPendingPointerMove = null;
             }
         }
-        
+
         void releasePendingTrackballLocked(Session s) {
             if (s.mPendingTrackballMove != null) {
                 mQueue.recycleEvent(s.mPendingTrackballMove);
                 s.mPendingTrackballMove = null;
             }
         }
-        
+
         MotionEvent finishedKey(Session session, IWindow client, boolean force,
                 int returnWhat) {
             if (DEBUG_INPUT) Log.v(
@@ -4767,7 +4884,7 @@
                     session.mPendingTrackballMove = null;
                     session.mPendingTrackballWindow = null;
                 }
-                
+
                 if (mLastBinder == client.asBinder()) {
                     if (DEBUG_INPUT) Log.v(
                         TAG, "finishedKey: last paused="
@@ -4783,7 +4900,7 @@
                         notifyAll();
                     }
                 }
-                
+
                 if (qev != null) {
                     MotionEvent res = (MotionEvent)qev.event;
                     if (DEBUG_INPUT) Log.v(TAG,
@@ -4803,7 +4920,7 @@
                 notifyAll();
             }
         }
-        
+
         void handleNewWindowLocked(WindowState newWindow) {
             if (!newWindow.canReceiveKeys()) {
                 return;
@@ -4904,7 +5021,7 @@
                 notifyAll();
             }
         }
-        
+
         void appSwitchComing() {
             synchronized (this) {
                 // Don't wait for more than .5 seconds for app to finish
@@ -4917,13 +5034,13 @@
                 notifyAll();
             }
         }
-        
+
         private final void doFinishedKeyLocked(boolean doRecycle) {
             if (mLastWin != null) {
                 releasePendingPointerLocked(mLastWin.mSession);
                 releasePendingTrackballLocked(mLastWin.mSession);
             }
-            
+
             if (mLastWin == null || !mLastWin.mToken.paused
                 || !mLastWin.isVisibleLw()) {
                 // If the current window has been paused, we aren't -really-
@@ -4939,7 +5056,7 @@
     private class KeyQ extends KeyInputQueue
             implements KeyInputQueue.FilterCallback {
         PowerManager.WakeLock mHoldingScreen;
-        
+
         KeyQ() {
             super(mContext);
             PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
@@ -4953,7 +5070,7 @@
             if (mPolicy.preprocessInputEventTq(event)) {
                 return true;
             }
-            
+
             switch (event.type) {
                 case RawInputEvent.EV_KEY: {
                     // XXX begin hack
@@ -4973,11 +5090,11 @@
                         }
                     }
                     // XXX end hack
-                    
+
                     boolean screenIsOff = !mPowerManager.screenIsOn();
                     boolean screenIsDim = !mPowerManager.screenIsBright();
                     int actions = mPolicy.interceptKeyTq(event, !screenIsOff);
-                    
+
                     if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
                         mPowerManager.goToSleep(event.when);
                     }
@@ -4992,7 +5109,7 @@
                         mPowerManager.userActivity(event.when, false,
                                 LocalPowerManager.BUTTON_EVENT, false);
                     }
-                    
+
                     if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
                         if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
                             filterQueue(this);
@@ -5003,7 +5120,7 @@
                         return false;
                     }
                 }
-                    
+
                 case RawInputEvent.EV_REL: {
                     boolean screenIsOff = !mPowerManager.screenIsOn();
                     boolean screenIsDim = !mPowerManager.screenIsBright();
@@ -5020,7 +5137,7 @@
                     }
                     return true;
                 }
-                
+
                 case RawInputEvent.EV_ABS: {
                     boolean screenIsOff = !mPowerManager.screenIsOn();
                     boolean screenIsDim = !mPowerManager.screenIsBright();
@@ -5037,7 +5154,7 @@
                     }
                     return true;
                 }
-                    
+
                 default:
                     return true;
             }
@@ -5057,7 +5174,7 @@
                     return FILTER_KEEP;
             }
         }
-        
+
         /**
          * Must be called with the main window manager lock held.
          */
@@ -5078,11 +5195,11 @@
         mSafeMode = mPolicy.detectSafeMode();
         return mSafeMode;
     }
-    
+
     public void systemReady() {
         mPolicy.systemReady();
     }
-    
+
     private final class InputDispatcherThread extends Thread {
         // Time to wait when there is nothing to do: 9999 seconds.
         static final int LONG_WAIT=9999*1000;
@@ -5090,7 +5207,7 @@
         public InputDispatcherThread() {
             super("InputDispatcher");
         }
-        
+
         @Override
         public void run() {
             while (true) {
@@ -5101,11 +5218,11 @@
                 }
             }
         }
-        
+
         private void process() {
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-            
+
             // The last key event we saw
             KeyEvent lastKey = null;
 
@@ -5113,12 +5230,12 @@
             long lastKeyTime = SystemClock.uptimeMillis();
             long nextKeyTime = lastKeyTime+LONG_WAIT;
 
-            // How many successive repeats we generated 
+            // How many successive repeats we generated
             int keyRepeatCount = 0;
 
             // Need to report that configuration has changed?
             boolean configChanged = false;
-            
+
             while (true) {
                 long curTime = SystemClock.uptimeMillis();
 
@@ -5199,14 +5316,14 @@
                                 mQueue.recycleEvent(ev);
                             break;
                         }
-                        
+
                     } else if (configChanged) {
                         configChanged = false;
                         sendNewConfiguration();
-                        
+
                     } else if (lastKey != null) {
                         curTime = SystemClock.uptimeMillis();
-                        
+
                         // Timeout occurred while key was down.  If it is at or
                         // past the key repeat time, dispatch the repeat.
                         if (DEBUG_INPUT) Log.v(
@@ -5215,7 +5332,7 @@
                         if (curTime < nextKeyTime) {
                             continue;
                         }
-    
+
                         lastKeyTime = nextKeyTime;
                         nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
                         keyRepeatCount++;
@@ -5223,14 +5340,14 @@
                             TAG, "Key repeat: count=" + keyRepeatCount
                             + ", next @ " + nextKeyTime);
                         dispatchKey(KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount), 0, 0);
-                        
+
                     } else {
                         curTime = SystemClock.uptimeMillis();
-                        
+
                         lastKeyTime = curTime;
                         nextKeyTime = curTime + LONG_WAIT;
                     }
-                    
+
                 } catch (Exception e) {
                     Log.e(TAG,
                         "Input thread received uncaught exception: " + e, e);
@@ -5253,14 +5370,14 @@
         SurfaceSession mSurfaceSession;
         int mNumWindow = 0;
         boolean mClientDead = false;
-        
+
         /**
          * Current pointer move event being dispatched to client window...  must
          * hold key lock to access.
          */
         QueuedEvent mPendingPointerMove;
         WindowState mPendingPointerWindow;
-        
+
         /**
          * Current trackball move event being dispatched to client window...  must
          * hold key lock to access.
@@ -5280,7 +5397,7 @@
             sb.append(mUid);
             sb.append("}");
             mStringName = sb.toString();
-            
+
             synchronized (mWindowMap) {
                 if (mInputMethodManager == null && mHaveInputMethods) {
                     IBinder b = ServiceManager.getService(
@@ -5311,7 +5428,7 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
-        
+
         @Override
         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                 throws RemoteException {
@@ -5336,6 +5453,7 @@
             } catch (RemoteException e) {
             }
             synchronized(mWindowMap) {
+                mClient.asBinder().unlinkToDeath(this, 0);
                 mClientDead = true;
                 killSessionLocked();
             }
@@ -5345,11 +5463,11 @@
                 int viewVisibility, Rect outContentInsets) {
             return addWindow(this, window, attrs, viewVisibility, outContentInsets);
         }
-        
+
         public void remove(IWindow window) {
             removeWindow(this, window);
         }
-        
+
         public int relayout(IWindow window, WindowManager.LayoutParams attrs,
                 int requestedWidth, int requestedHeight, int viewFlags,
                 boolean insetsPending, Rect outFrame, Rect outContentInsets,
@@ -5358,21 +5476,21 @@
                     requestedWidth, requestedHeight, viewFlags, insetsPending,
                     outFrame, outContentInsets, outVisibleInsets, outSurface);
         }
-        
+
         public void setTransparentRegion(IWindow window, Region region) {
             setTransparentRegionWindow(this, window, region);
         }
-        
+
         public void setInsets(IWindow window, int touchableInsets,
                 Rect contentInsets, Rect visibleInsets) {
             setInsetsWindow(this, window, touchableInsets, contentInsets,
                     visibleInsets);
         }
-        
+
         public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
             getWindowDisplayFrame(this, window, outDisplayFrame);
         }
-        
+
         public void finishDrawing(IWindow window) {
             if (localLOGV) Log.v(
                 TAG, "IWindow finishDrawing called for " + window);
@@ -5392,7 +5510,7 @@
             return mKeyWaiter.finishedKey(this, window, false,
                     KeyWaiter.RETURN_PENDING_POINTER);
         }
-        
+
         public MotionEvent getPendingTrackballMove(IWindow window) {
             if (localLOGV) Log.v(
                     TAG, "IWindow getPendingMotionEvent called for " + window);
@@ -5424,7 +5542,7 @@
                 }
             }
         }
-        
+
         void windowAddedLocked() {
             if (mSurfaceSession == null) {
                 if (localLOGV) Log.v(
@@ -5439,7 +5557,7 @@
             mNumWindow--;
             killSessionLocked();
         }
-    
+
         void killSessionLocked() {
             if (mNumWindow <= 0 && mClientDead) {
                 mSessions.remove(this);
@@ -5458,7 +5576,7 @@
                 }
             }
         }
-        
+
         void dump(PrintWriter pw, String prefix) {
             pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
                     pw.print(" mClientDead="); pw.print(mClientDead);
@@ -5519,11 +5637,11 @@
         boolean mHaveFrame;
 
         WindowState mNextOutsideTouch;
-        
+
         // Actual frame shown on-screen (may be modified by animation)
         final Rect mShownFrame = new Rect();
         final Rect mLastShownFrame = new Rect();
-        
+
         /**
          * Insets that determine the actually visible area
          */
@@ -5543,19 +5661,19 @@
          * given internal insets before laying out other windows based on it.
          */
         boolean mGivenInsetsPending;
-        
+
         /**
          * These are the content insets that were given during layout for
          * this window, to be applied to windows behind it.
          */
         final Rect mGivenContentInsets = new Rect();
-        
+
         /**
          * These are the visible insets that were given during layout for
          * this window, to be applied to windows behind it.
          */
         final Rect mGivenVisibleInsets = new Rect();
-        
+
         /**
          * Flag indicating whether the touchable region should be adjusted by
          * the visible insets; if false the area outside the visible insets is
@@ -5563,7 +5681,7 @@
          * tests.
          */
         int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
-        
+
         // Current transformation being applied.
         float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
         float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
@@ -5602,7 +5720,7 @@
         // where we don't yet have a surface, but should have one soon, so
         // we can give the window focus before waiting for the relayout.
         boolean mRelayoutCalled;
-        
+
         // This is set after the Surface has been created but before the
         // window has been drawn.  During this time the surface is hidden.
         boolean mDrawPending;
@@ -5617,7 +5735,7 @@
         // to delay showing the surface until all windows in a token are ready
         // to be shown.
         boolean mReadyToShow;
-        
+
         // Set when the window has been shown in the screen the first time.
         boolean mHasDrawn;
 
@@ -5626,17 +5744,17 @@
 
         // Currently on the mDestroySurface list?
         boolean mDestroying;
-        
+
         // Completely remove from window manager after exit animation?
         boolean mRemoveOnExit;
 
         // Set when the orientation is changing and this window has not yet
         // been updated for the new orientation.
         boolean mOrientationChanging;
-        
+
         // Is this window now (or just being) removed?
         boolean mRemoved;
-        
+
         WindowState(Session s, IWindow c, WindowToken token,
                WindowState attachedWindow, WindowManager.LayoutParams a,
                int viewVisibility) {
@@ -5662,7 +5780,7 @@
                 return;
             }
             mDeathRecipient = deathRecipient;
-            
+
             if ((mAttrs.type >= FIRST_SUB_WINDOW &&
                     mAttrs.type <= LAST_SUB_WINDOW)) {
                 // The multiplier here is to reserve space for multiple
@@ -5738,7 +5856,7 @@
                 w = mAttrs.width == mAttrs.FILL_PARENT ? pw : mRequestedWidth;
                 h = mAttrs.height== mAttrs.FILL_PARENT ? ph : mRequestedHeight;
             }
-            
+
             final Rect container = mContainingFrame;
             container.set(pf);
 
@@ -5747,12 +5865,12 @@
 
             final Rect content = mContentFrame;
             content.set(cf);
-            
+
             final Rect visible = mVisibleFrame;
             visible.set(vf);
-            
+
             final Rect frame = mFrame;
-            
+
             //System.out.println("In: w=" + w + " h=" + h + " container=" +
             //                   container + " x=" + mAttrs.x + " y=" + mAttrs.y);
 
@@ -5764,7 +5882,7 @@
 
             // Now make sure the window fits in the overall display.
             Gravity.applyDisplay(mAttrs.gravity, df, frame);
-            
+
             // Make sure the content and visible frames are inside of the
             // final window frame.
             if (content.left < frame.left) content.left = frame.left;
@@ -5775,19 +5893,19 @@
             if (visible.top < frame.top) visible.top = frame.top;
             if (visible.right > frame.right) visible.right = frame.right;
             if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;
-            
+
             final Rect contentInsets = mContentInsets;
             contentInsets.left = content.left-frame.left;
             contentInsets.top = content.top-frame.top;
             contentInsets.right = frame.right-content.right;
             contentInsets.bottom = frame.bottom-content.bottom;
-            
+
             final Rect visibleInsets = mVisibleInsets;
             visibleInsets.left = visible.left-frame.left;
             visibleInsets.top = visible.top-frame.top;
             visibleInsets.right = frame.right-visible.right;
             visibleInsets.bottom = frame.bottom-visible.bottom;
-            
+
             if (localLOGV) {
                 //if ("com.google.android.youtube".equals(mAttrs.packageName)
                 //        && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
@@ -5800,7 +5918,7 @@
                 //}
             }
         }
-        
+
         public Rect getFrameLw() {
             return mFrame;
         }
@@ -5828,11 +5946,11 @@
         public Rect getGivenContentInsetsLw() {
             return mGivenContentInsets;
         }
-        
+
         public Rect getGivenVisibleInsetsLw() {
             return mGivenVisibleInsets;
         }
-        
+
         public WindowManager.LayoutParams getAttrs() {
             return mAttrs;
         }
@@ -5840,7 +5958,7 @@
         public int getSurfaceLayer() {
             return mLayer;
         }
-        
+
         public IApplicationToken getAppToken() {
             return mAppToken != null ? mAppToken.appToken : null;
         }
@@ -5874,7 +5992,7 @@
                 mAnimation = null;
             }
         }
-        
+
         Surface createSurfaceLocked() {
             if (mSurface == null) {
                 mDrawPending = true;
@@ -5914,7 +6032,7 @@
 
                 try {
                     mSurface = new Surface(
-                            mSession.mSurfaceSession, mSession.mPid, 
+                            mSession.mSurfaceSession, mSession.mPid,
                             0, w, h, mAttrs.format, flags);
                 } catch (Surface.OutOfResourcesException e) {
                     Log.w(TAG, "OutOfResourcesException creating surface");
@@ -5924,7 +6042,7 @@
                     Log.e(TAG, "Exception creating surface", e);
                     return null;
                 }
-                
+
                 if (localLOGV) Log.v(
                     TAG, "Got surface: " + mSurface
                     + ", set left=" + mFrame.left + " top=" + mFrame.top
@@ -5961,7 +6079,7 @@
             }
             return mSurface;
         }
-        
+
         void destroySurfaceLocked() {
             // Window is no longer on-screen, so can no longer receive
             // key events...  if we were waiting for it to finish
@@ -5974,7 +6092,7 @@
             if (mAppToken != null && this == mAppToken.startingWindow) {
                 mAppToken.startingDisplayed = false;
             }
-            
+
             if (localLOGV) Log.v(
                 TAG, "Window " + this
                 + " destroying surface " + mSurface + ", session " + mSession);
@@ -6064,7 +6182,7 @@
                 enableScreenIfNeededLocked();
 
                 applyEnterAnimationLocked(this);
-                
+
                 int i = mChildWindows.size();
                 while (i > 0) {
                     i--;
@@ -6074,7 +6192,7 @@
                         c.performShowLocked();
                     }
                 }
-                
+
                 if (mAttrs.type != TYPE_APPLICATION_STARTING
                         && mAppToken != null) {
                     mAppToken.firstWindowDrawn = true;
@@ -6090,13 +6208,13 @@
             }
             return true;
         }
-        
+
         // This must be called while inside a transaction.  Returns true if
         // there is more animation to run.
         boolean stepAnimationLocked(long currentTime, int dw, int dh) {
             if (!mDisplayFrozen) {
                 // We will run animations as long as the display isn't frozen.
-                
+
                 if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
                     mHasTransformation = true;
                     mHasLocalTransformation = true;
@@ -6154,7 +6272,7 @@
                 mLocalAnimating = true;
                 mAnimation = null;
             }
-            
+
             if (!mAnimating && !mLocalAnimating) {
                 return false;
             }
@@ -6163,7 +6281,7 @@
                 TAG, "Animation done in " + this + ": exiting=" + mExiting
                 + ", reportedVisible="
                 + (mAppToken != null ? mAppToken.reportedVisible : false));
-            
+
             mAnimating = false;
             mLocalAnimating = false;
             mAnimation = null;
@@ -6187,7 +6305,7 @@
                 mFinishedStarting.add(mAppToken);
                 mH.sendEmptyMessage(H.FINISHED_STARTING);
             }
-            
+
             finishExit();
 
             if (mAppToken != null) {
@@ -6203,16 +6321,16 @@
                     + ": exiting=" + mExiting
                     + " remove=" + mRemoveOnExit
                     + " windowAnimating=" + isWindowAnimating());
-            
+
             final int N = mChildWindows.size();
             for (int i=0; i<N; i++) {
                 ((WindowState)mChildWindows.get(i)).finishExit();
             }
-            
+
             if (!mExiting) {
                 return;
             }
-            
+
             if (isWindowAnimating()) {
                 return;
             }
@@ -6239,7 +6357,7 @@
                 mRemoveOnExit = false;
             }
         }
-        
+
         boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
             if (dsdx < .99999f || dsdx > 1.00001f) return false;
             if (dtdy < .99999f || dtdy > 1.00001f) return false;
@@ -6247,7 +6365,7 @@
             if (dsdy < -.000001f || dsdy > .000001f) return false;
             return true;
         }
-        
+
         void computeShownFrameLocked() {
             final boolean selfTransformation = mHasLocalTransformation;
             Transformation attachedTransformation =
@@ -6258,7 +6376,7 @@
                     ? mAppToken.transformation : null;
             if (selfTransformation || attachedTransformation != null
                     || appTransformation != null) {
-                // cache often used attributes locally  
+                // cache often used attributes locally
                 final Rect frame = mFrame;
                 final float tmpFloats[] = mTmpFloats;
                 final Matrix tmpMatrix = mTmpMatrix;
@@ -6280,7 +6398,7 @@
                 // Here we must not transform the position of the surface
                 // since it is already included in the transformation.
                 //Log.i(TAG, "Transform: " + matrix);
-                
+
                 tmpMatrix.getValues(tmpFloats);
                 mDsDx = tmpFloats[Matrix.MSCALE_X];
                 mDtDx = tmpFloats[Matrix.MSKEW_X];
@@ -6315,14 +6433,14 @@
                 } else {
                     //Log.i(TAG, "Not applying alpha transform");
                 }
-                
+
                 if (localLOGV) Log.v(
                     TAG, "Continuing animation in " + this +
                     ": " + mShownFrame +
                     ", alpha=" + mTransformation.getAlpha());
                 return;
             }
-            
+
             mShownFrame.set(mFrame);
             mShownAlpha = mAlpha;
             mDsDx = 1;
@@ -6330,7 +6448,7 @@
             mDsDy = 0;
             mDtDy = 1;
         }
-        
+
         /**
          * Is this window visible?  It is not visible if there is no
          * surface, or we are in the process of running an exit animation
@@ -6393,7 +6511,7 @@
                         && (!mAttachedHidden || mAnimating);
             }
         }
-        
+
         /**
          * Like isOnScreen(), but we don't return true if the window is part
          * of a transition that has not yet been started.
@@ -6412,7 +6530,7 @@
             final AppWindowToken atoken = mAppToken;
             return mAnimation != null
                     || (attached != null && attached.mAnimation != null)
-                    || (atoken != null && 
+                    || (atoken != null &&
                             (atoken.animation != null
                                     || atoken.inPendingTransaction));
         }
@@ -6454,7 +6572,7 @@
             }
             return false;
         }
-        
+
         boolean isFullscreenOpaque(int screenWidth, int screenHeight) {
             if (mAttrs.format != PixelFormat.OPAQUE || mSurface == null
                     || mAnimation != null || mDrawPending || mCommitDrawPending) {
@@ -6546,7 +6664,7 @@
 
         void dump(PrintWriter pw, String prefix) {
             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);
@@ -6662,7 +6780,7 @@
                 + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}";
         }
     }
-    
+
     // -------------------------------------------------------------
     // Window Token State
     // -------------------------------------------------------------
@@ -6673,17 +6791,17 @@
 
         // The type of window this token is for, as per WindowManager.LayoutParams.
         final int windowType;
-        
+
         // Set if this token was explicitly added by a client, so should
         // not be removed when all windows are removed.
         final boolean explicit;
-        
+
         // For printing.
         String stringName;
-        
+
         // If this is an AppWindowToken, this is non-null.
         AppWindowToken appWindowToken;
-        
+
         // All of the windows associated with this token.
         final ArrayList<WindowState> windows = new ArrayList<WindowState>();
 
@@ -6734,7 +6852,7 @@
         int groupId = -1;
         boolean appFullscreen;
         int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-        
+
         // These are used for determining when all windows associated with
         // an activity have been drawn, so they can be made visible together
         // at the same time.
@@ -6743,20 +6861,20 @@
         int numDrawnWindows;
         boolean inPendingTransaction;
         boolean allDrawn;
-        
+
         // Is this token going to be hidden in a little while?  If so, it
         // won't be taken into account for setting the screen orientation.
         boolean willBeHidden;
-        
+
         // Is this window's surface needed?  This is almost like hidden, except
         // it will sometimes be true a little earlier: when the token has
         // been shown, but is still waiting for its app transition to execute
         // before making its windows shown.
         boolean hiddenRequested;
-        
+
         // Have we told the window clients to hide themselves?
         boolean clientHidden;
-        
+
         // Last visibility state we reported to the app token.
         boolean reportedVisible;
 
@@ -6765,16 +6883,16 @@
 
         // Have we been asked to have this token keep the screen frozen?
         boolean freezingScreen;
-        
+
         boolean animating;
         Animation animation;
         boolean hasTransformation;
         final Transformation transformation = new Transformation();
-        
+
         // Offset to the window of all layers in the token, for use by
         // AppWindowToken animations.
         int animLayerAdjustment;
-        
+
         // Information about an application starting window if displayed.
         StartingData startingData;
         WindowState startingWindow;
@@ -6789,7 +6907,7 @@
             appWindowToken = this;
             appToken = _token;
         }
-        
+
         public void setAnimation(Animation anim) {
             if (localLOGV) Log.v(
                 TAG, "Setting animation in " + this + ": " + anim);
@@ -6804,13 +6922,13 @@
             } else if (zorder == Animation.ZORDER_BOTTOM) {
                 adj = -TYPE_LAYER_OFFSET;
             }
-            
+
             if (animLayerAdjustment != adj) {
                 animLayerAdjustment = adj;
                 updateLayers();
             }
         }
-        
+
         public void setDummyAnimation() {
             if (animation == null) {
                 if (localLOGV) Log.v(
@@ -6825,7 +6943,7 @@
                 animating = true;
             }
         }
-        
+
         void updateLayers() {
             final int N = allAppWindows.size();
             final int adj = animLayerAdjustment;
@@ -6839,7 +6957,7 @@
                 }
             }
         }
-        
+
         void sendAppVisibilityToClients() {
             final int N = allAppWindows.size();
             for (int i=0; i<N; i++) {
@@ -6856,7 +6974,7 @@
                 }
             }
         }
-        
+
         void showAllWindowsLocked() {
             final int NW = allAppWindows.size();
             for (int i=0; i<NW; i++) {
@@ -6866,12 +6984,12 @@
                 w.performShowLocked();
             }
         }
-        
+
         // This must be called while inside a transaction.
         boolean stepAnimationLocked(long currentTime, int dw, int dh) {
             if (!mDisplayFrozen) {
                 // We will run animations as long as the display isn't frozen.
-                
+
                 if (animation == sDummyAnimation) {
                     // This guy is going to animate, but not yet.  For now count
                     // it is not animating for purposes of scheduling transactions;
@@ -6879,7 +6997,7 @@
                     // a real animation and the next call will execute normally.
                     return false;
                 }
-                
+
                 if ((allDrawn || animating || startingDisplayed) && animation != null) {
                     if (!animating) {
                         if (DEBUG_ANIM) Log.v(
@@ -6915,7 +7033,7 @@
             }
 
             hasTransformation = false;
-            
+
             if (!animating) {
                 return false;
             }
@@ -6925,7 +7043,7 @@
             if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == this) {
                 moveInputMethodWindowsIfNeededLocked(true);
             }
-            
+
             if (DEBUG_ANIM) Log.v(
                     TAG, "Animation done in " + this
                     + ": reportedVisible=" + reportedVisible);
@@ -6935,13 +7053,13 @@
                 animLayerAdjustment = 0;
                 updateLayers();
             }
-            
+
             final int N = windows.size();
             for (int i=0; i<N; i++) {
                 ((WindowState)windows.get(i)).finishExit();
             }
             updateReportedVisibilityLocked();
-            
+
             return false;
         }
 
@@ -6949,11 +7067,11 @@
             if (appToken == null) {
                 return;
             }
-            
+
             int numInteresting = 0;
             int numVisible = 0;
             boolean nowGone = true;
-            
+
             if (DEBUG_VISIBILITY) Log.v(TAG, "Update reported visibility: " + this);
             final int N = allAppWindows.size();
             for (int i=0; i<N; i++) {
@@ -6987,7 +7105,7 @@
                     nowGone = false;
                 }
             }
-            
+
             boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
             if (DEBUG_VISIBILITY) Log.v(TAG, "VIS " + this + ": interesting="
                     + numInteresting + " visible=" + numVisible);
@@ -7004,7 +7122,7 @@
                     mH.sendMessage(m);
             }
         }
-        
+
         void dump(PrintWriter pw, String prefix) {
             super.dump(pw, prefix);
             if (appToken != null) {
@@ -7069,7 +7187,7 @@
             return stringName;
         }
     }
-    
+
     public static WindowManager.LayoutParams findAnimations(
             ArrayList<AppWindowToken> order,
             ArrayList<AppWindowToken> tokenList1,
@@ -7077,7 +7195,7 @@
         // We need to figure out which animation to use...
         WindowManager.LayoutParams animParams = null;
         int animSrc = 0;
-        
+
         //Log.i(TAG, "Looking for animations...");
         for (int i=order.size()-1; i>=0; i--) {
             AppWindowToken wtoken = order.get(i);
@@ -7106,10 +7224,10 @@
                 }
             }
         }
-        
+
         return animParams;
     }
-    
+
     // -------------------------------------------------------------
     // DummyAnimation
     // -------------------------------------------------------------
@@ -7123,7 +7241,7 @@
         }
     }
     static final Animation sDummyAnimation = new DummyAnimation();
-    
+
     // -------------------------------------------------------------
     // Async Handler
     // -------------------------------------------------------------
@@ -7134,7 +7252,7 @@
         final CharSequence nonLocalizedLabel;
         final int labelRes;
         final int icon;
-        
+
         StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
                 int _labelRes, int _icon) {
             pkg = _pkg;
@@ -7161,19 +7279,19 @@
         public static final int ENABLE_SCREEN = 16;
         public static final int APP_FREEZE_TIMEOUT = 17;
         public static final int COMPUTE_AND_SEND_NEW_CONFIGURATION = 18;
-        
+
         private Session mLastReportedHold;
-        
+
         public H() {
         }
-        
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case REPORT_FOCUS_CHANGE: {
                     WindowState lastFocus;
                     WindowState newFocus;
-    
+
                     synchronized(mWindowMap) {
                         lastFocus = mLastFocus;
                         newFocus = mCurrentFocus;
@@ -7217,7 +7335,7 @@
 
                 case REPORT_LOSING_FOCUS: {
                     ArrayList<WindowState> losers;
-    
+
                     synchronized(mWindowMap) {
                         losers = mLosingFocus;
                         mLosingFocus = new ArrayList<WindowState>();
@@ -7249,10 +7367,10 @@
                         // Animation has been canceled... do nothing.
                         return;
                     }
-                    
+
                     if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Add starting "
                             + wtoken + ": pkg=" + sd.pkg);
-                    
+
                     View view = null;
                     try {
                         view = mPolicy.addStartingWindow(
@@ -7379,7 +7497,7 @@
                     } catch (RemoteException ex) {
                     }
                 } break;
-                
+
                 case WINDOW_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
                         Log.w(TAG, "Window freeze timeout expired.");
@@ -7396,7 +7514,7 @@
                     }
                     break;
                 }
-                
+
                 case HOLD_SCREEN_CHANGED: {
                     Session oldHold;
                     Session newHold;
@@ -7405,7 +7523,7 @@
                         newHold = (Session)msg.obj;
                         mLastReportedHold = newHold;
                     }
-                    
+
                     if (oldHold != newHold) {
                         try {
                             if (oldHold != null) {
@@ -7423,7 +7541,7 @@
                     }
                     break;
                 }
-                
+
                 case APP_TRANSITION_TIMEOUT: {
                     synchronized (mWindowMap) {
                         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
@@ -7436,7 +7554,7 @@
                     }
                     break;
                 }
-                
+
                 case PERSIST_ANIMATION_SCALE: {
                     Settings.System.putFloat(mContext.getContentResolver(),
                             Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
@@ -7444,7 +7562,7 @@
                             Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
                     break;
                 }
-                
+
                 case FORCE_GC: {
                     synchronized(mWindowMap) {
                         if (mAnimationPending) {
@@ -7464,12 +7582,12 @@
                     Runtime.getRuntime().gc();
                     break;
                 }
-                
+
                 case ENABLE_SCREEN: {
                     performEnableScreen();
                     break;
                 }
-                
+
                 case APP_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
                         Log.w(TAG, "App freeze timeout expired.");
@@ -7485,14 +7603,14 @@
                     }
                     break;
                 }
-                
+
                 case COMPUTE_AND_SEND_NEW_CONFIGURATION: {
-                    if (updateOrientationFromAppTokens(null, null) != null) {
+                    if (updateOrientationFromAppTokensUnchecked(null, null) != null) {
                         sendNewConfiguration();
                     }
                     break;
                 }
-                
+
             }
         }
     }
@@ -7526,7 +7644,7 @@
         }
         return false;
     }
-    
+
     // -------------------------------------------------------------
     // Internals
     // -------------------------------------------------------------
@@ -7534,7 +7652,7 @@
     final WindowState windowForClientLocked(Session session, IWindow client) {
         return windowForClientLocked(session, client.asBinder());
     }
-    
+
     final WindowState windowForClientLocked(Session session, IBinder client) {
         WindowState win = mWindowMap.get(client);
         if (localLOGV) Log.v(
@@ -7559,7 +7677,7 @@
         int curBaseLayer = 0;
         int curLayer = 0;
         int i;
-        
+
         for (i=0; i<N; i++) {
             WindowState w = (WindowState)mWindows.get(i);
             if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) {
@@ -7615,11 +7733,11 @@
                 }
             }
         }
-        
+
         mInLayout = true;
         try {
             performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
-            
+
             int i = mPendingRemove.size()-1;
             if (i >= 0) {
                 while (i >= 0) {
@@ -7655,7 +7773,7 @@
         int i;
 
         // FIRST LOOP: Perform a layout, if needed.
-        
+
         while (mLayoutNeeded) {
             mPolicy.beginLayoutLw(dw, dh);
 
@@ -7690,7 +7808,7 @@
                     }
                 }
             }
-            
+
             // Now perform layout of attached windows, which usually
             // depend on the position of the window they are attached to.
             // XXX does not deal with windows that are attached to windows
@@ -7721,7 +7839,7 @@
             }
         }
     }
-    
+
     private final void performLayoutAndPlaceSurfacesLockedInner(
             boolean recoveringMemory) {
         final long currentTime = SystemClock.uptimeMillis();
@@ -7732,13 +7850,12 @@
         int i;
 
         // FIRST LOOP: Perform a layout, if needed.
-        
         performLayoutLockedInner();
-        
+
         if (mFxSession == null) {
             mFxSession = new SurfaceSession();
         }
-        
+
         if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION");
 
         // Initialize state of exiting tokens.
@@ -7752,7 +7869,6 @@
         }
 
         // SECOND LOOP: Execute animations and update visibility of windows.
-        
         boolean orientationChangeComplete = true;
         Session holdScreen = null;
         float screenBrightness = -1;
@@ -8063,7 +8179,7 @@
                             !w.mLastContentInsets.equals(w.mContentInsets);
                         w.mVisibleInsetsChanged =
                             !w.mLastVisibleInsets.equals(w.mVisibleInsets);
-                        if (!w.mLastFrame.equals(w.mFrame) 
+                        if (!w.mLastFrame.equals(w.mFrame)
                                 || w.mContentInsetsChanged
                                 || w.mVisibleInsetsChanged) {
                             w.mLastFrame.set(w.mFrame);
@@ -8085,7 +8201,7 @@
                                     w.mAppToken.allDrawn = false;
                                 }
                             }
-                            if (DEBUG_ORIENTATION) Log.v(TAG, 
+                            if (DEBUG_ORIENTATION) Log.v(TAG,
                                     "Resizing window " + w + " to " + w.mFrame);
                             mResizingWindows.add(w);
                         } else if (w.mOrientationChanging) {
@@ -8275,7 +8391,7 @@
                                     if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM "
                                             + mDimSurface + ": CREATE");
                                     try {
-                                        mDimSurface = new Surface(mFxSession, 0, 
+                                        mDimSurface = new Surface(mFxSession, 0,
                                                 -1, 16, 16,
                                                 PixelFormat.OPAQUE,
                                                 Surface.FX_SURFACE_DIM);
@@ -8330,7 +8446,7 @@
                                     if (SHOW_TRANSACTIONS) Log.i(TAG, "  BLUR "
                                             + mBlurSurface + ": CREATE");
                                     try {
-                                        mBlurSurface = new Surface(mFxSession, 0, 
+                                        mBlurSurface = new Surface(mFxSession, 0,
                                                 -1, 16, 16,
                                                 PixelFormat.OPAQUE,
                                                 Surface.FX_SURFACE_BLUR);
@@ -8384,7 +8500,7 @@
                 } else {
                     more = false;
                 }
-                
+
                 // Do we need to continue animating?
                 if (more) {
                     if (SHOW_TRANSACTIONS) Log.i(TAG, "  DIM "
@@ -8410,7 +8526,7 @@
                     }
                 }
             }
-            
+
             if (!blurring && mBlurShown) {
                 if (SHOW_TRANSACTIONS) Log.i(TAG, "  BLUR " + mBlurSurface
                         + ": HIDE");
@@ -8428,7 +8544,7 @@
         }
 
         Surface.closeTransaction();
-        
+
         if (DEBUG_ORIENTATION && mDisplayFrozen) Log.v(TAG,
                 "With display frozen, orientationChangeComplete="
                 + orientationChangeComplete);
@@ -8441,7 +8557,7 @@
                 stopFreezingDisplayLocked();
             }
         }
-        
+
         i = mResizingWindows.size();
         if (i > 0) {
             do {
@@ -8459,7 +8575,7 @@
             } while (i > 0);
             mResizingWindows.clear();
         }
-        
+
         // Destroy the surface of any windows that are no longer visible.
         i = mDestroySurface.size();
         if (i > 0) {
@@ -8518,13 +8634,13 @@
             mH.sendMessageDelayed(mH.obtainMessage(H.ANIMATE), delay);
         }
     }
-    
+
     /**
      * Have the surface flinger show a surface, robustly dealing with
      * error conditions.  In particular, if there is not enough memory
      * to show the surface, then we will try to get rid of other surfaces
      * in order to succeed.
-     * 
+     *
      * @return Returns true if the surface was successfully shown.
      */
     boolean showSurfaceRobustlyLocked(WindowState win) {
@@ -8536,22 +8652,22 @@
         } catch (RuntimeException e) {
             Log.w(TAG, "Failure showing surface " + win.mSurface + " in " + win);
         }
-        
+
         reclaimSomeSurfaceMemoryLocked(win, "show");
-        
+
         return false;
     }
-    
+
     void reclaimSomeSurfaceMemoryLocked(WindowState win, String operation) {
         final Surface surface = win.mSurface;
-        
+
         EventLog.writeEvent(LOG_WM_NO_SURFACE_MEMORY, win.toString(),
                 win.mSession.mPid, operation);
-        
+
         if (mForceRemoves == null) {
             mForceRemoves = new ArrayList<WindowState>();
         }
-        
+
         long callingIdentity = Binder.clearCallingIdentity();
         try {
             // There was some problem...   first, do a sanity check of the
@@ -8585,7 +8701,7 @@
                     }
                 }
             }
-            
+
             boolean killedApps = false;
             if (!leakedSurface) {
                 Log.w(TAG, "No leaked surfaces; killing applicatons!");
@@ -8609,7 +8725,7 @@
                     }
                 }
             }
-            
+
             if (leakedSurface || killedApps) {
                 // We managed to reclaim some memory, so get rid of the trouble
                 // surface and ask the app to request another one.
@@ -8618,7 +8734,7 @@
                     surface.clear();
                     win.mSurface = null;
                 }
-                
+
                 try {
                     win.mClient.dispatchGetNewSurface();
                 } catch (RemoteException e) {
@@ -8628,7 +8744,7 @@
             Binder.restoreCallingIdentity(callingIdentity);
         }
     }
-    
+
     private boolean updateFocusedWindowLocked(int mode) {
         WindowState newFocus = computeFocusedWindowLocked();
         if (mCurrentFocus != newFocus) {
@@ -8641,7 +8757,7 @@
             final WindowState oldFocus = mCurrentFocus;
             mCurrentFocus = newFocus;
             mLosingFocus.remove(newFocus);
-            
+
             final WindowState imWindow = mInputMethodWindow;
             if (newFocus != imWindow && oldFocus != imWindow) {
                 if (moveInputMethodWindowsIfNeededLocked(
@@ -8657,7 +8773,7 @@
                     assignLayersLocked();
                 }
             }
-            
+
             if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
                 mKeyWaiter.handleNewWindowLocked(newFocus);
             }
@@ -8685,13 +8801,13 @@
                 + ", canReceive=" + win.canReceiveKeys());
 
             AppWindowToken thisApp = win.mAppToken;
-            
+
             // If this window's application has been removed, just skip it.
             if (thisApp != null && thisApp.removed) {
                 i--;
                 continue;
             }
-            
+
             // If there is a focused app, don't allow focus to go to any
             // windows below it.  If this is an application window, step
             // through the app tokens until we find its app.
@@ -8748,9 +8864,9 @@
             }
             return;
         }
-        
+
         mScreenFrozenLock.acquire();
-        
+
         long now = SystemClock.uptimeMillis();
         //Log.i(TAG, "Freezing, gc pending: " + mFreezeGcPending + ", now " + now);
         if (mFreezeGcPending != 0) {
@@ -8763,32 +8879,32 @@
         } else {
             mFreezeGcPending = now;
         }
-        
+
         mDisplayFrozen = true;
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
             mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
             mAppTransitionReady = true;
         }
-        
+
         if (PROFILE_ORIENTATION) {
             File file = new File("/data/system/frozen");
             Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
         }
         Surface.freezeDisplay(0);
     }
-    
+
     private void stopFreezingDisplayLocked() {
         if (!mDisplayFrozen) {
             return;
         }
-        
+
         mDisplayFrozen = false;
         mH.removeMessages(H.APP_FREEZE_TIMEOUT);
         if (PROFILE_ORIENTATION) {
             Debug.stopMethodTracing();
         }
         Surface.unfreezeDisplay(0);
-        
+
         // Reset the key delivery timeout on unfreeze, too.  We force a wakeup here
         // too because regular key delivery processing should resume immediately.
         synchronized (mKeyWaiter) {
@@ -8804,10 +8920,10 @@
         mH.removeMessages(H.FORCE_GC);
         mH.sendMessageDelayed(mH.obtainMessage(H.FORCE_GC),
                 2000);
-        
+
         mScreenFrozenLock.release();
     }
-    
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
@@ -8817,7 +8933,7 @@
                     + ", uid=" + Binder.getCallingUid());
             return;
         }
-        
+
         synchronized(mWindowMap) {
             pw.println("Current Window Manager state:");
             for (int i=mWindows.size()-1; i>=0; i--) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 9471eff..2fe4dd4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,7 +17,7 @@
 package com.android.server.am;
 
 import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.os.RuntimeInit;
+import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
 import com.android.server.ProcessMap;
 import com.android.server.ProcessStats;
@@ -30,22 +30,25 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
 import android.app.AlertDialog;
+import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IActivityWatcher;
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
-import android.app.IIntentReceiver;
-import android.app.IIntentSender;
 import android.app.IServiceConnection;
 import android.app.IThumbnailReceiver;
 import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.ResultInfo;
+import android.backup.IBackupManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
@@ -78,6 +81,9 @@
 import android.os.SystemProperties;
 import android.provider.Checkin;
 import android.provider.Settings;
+import android.server.data.CrashData;
+import android.server.data.StackTraceElementData;
+import android.server.data.ThrowableData;
 import android.text.TextUtils;
 import android.util.Config;
 import android.util.EventLog;
@@ -92,10 +98,13 @@
 
 import dalvik.system.Zygote;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
@@ -117,11 +126,13 @@
     static final boolean DEBUG_OOM_ADJ = localLOGV || false;
     static final boolean DEBUG_TRANSITION = localLOGV || false;
     static final boolean DEBUG_BROADCAST = localLOGV || false;
+    static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
     static final boolean DEBUG_SERVICE = localLOGV || false;
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PROCESSES = localLOGV || false;
     static final boolean DEBUG_USER_LEAVING = localLOGV || false;
     static final boolean DEBUG_RESULTS = localLOGV || false;
+    static final boolean DEBUG_BACKUP = localLOGV || true;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -191,6 +202,10 @@
     // Maximum number of recent tasks that we can remember.
     static final int MAX_RECENT_TASKS = 20;
 
+    // Amount of time after a call to stopAppSwitches() during which we will
+    // prevent further untrusted switches from happening.
+    static final long APP_SWITCH_DELAY_TIME = 5*1000;
+    
     // How long until we reset a task when the user returns to it.  Currently
     // 30 minutes.
     static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
@@ -273,6 +288,10 @@
     // because the user interacts with it so much.
     final int HOME_APP_ADJ;
 
+    // This is a process currently hosting a backup operation.  Killing it
+    // is not entirely fatal but is generally a bad idea.
+    final int BACKUP_APP_ADJ;
+
     // This is a process holding a secondary server -- killing it will not
     // have much of an impact as far as the user is concerned. Value set in
     // system/rootdir/init.rc on startup.
@@ -301,6 +320,7 @@
     final int EMPTY_APP_MEM;
     final int HIDDEN_APP_MEM;
     final int HOME_APP_MEM;
+    final int BACKUP_APP_MEM;
     final int SECONDARY_SERVER_MEM;
     final int VISIBLE_APP_MEM;
     final int FOREGROUND_APP_MEM;
@@ -328,6 +348,21 @@
     final ArrayList mHistory = new ArrayList();
 
     /**
+     * Description of a request to start a new activity, which has been held
+     * due to app switches being disabled.
+     */
+    class PendingActivityLaunch {
+        HistoryRecord r;
+        HistoryRecord sourceRecord;
+        Uri[] grantedUriPermissions;
+        int grantedMode;
+        boolean onlyIfNeeded;
+    }
+    
+    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
+            = new ArrayList<PendingActivityLaunch>();
+    
+    /**
      * List of all active broadcasts that are to be executed immediately
      * (without waiting for another broadcast to finish).  Currently this only
      * contains broadcasts to registered receivers, to avoid spinning up
@@ -605,6 +640,12 @@
             = new ArrayList<ServiceRecord>();
 
     /**
+     * Backup/restore process management
+     */
+    String mBackupAppName = null;
+    BackupRecord mBackupTarget = null;
+
+    /**
      * List of PendingThumbnailsRecord objects of clients who are still
      * waiting to receive all of the thumbnails for a task.
      */
@@ -704,6 +745,20 @@
 
     int mFactoryTest;
 
+    boolean mCheckedForSetup;
+    
+    /**
+     * The time at which we will allow normal application switches again,
+     * after a call to {@link #stopAppSwitches()}.
+     */
+    long mAppSwitchesAllowedTime;
+
+    /**
+     * This is set to true after the first switch after mAppSwitchesAllowedTime
+     * is set; any switches after that will clear the time.
+     */
+    boolean mDidAppSwitch;
+    
     /**
      * Set while we are wanting to sleep, to prevent any
      * activities from being started/resumed.
@@ -757,6 +812,12 @@
      */
     int[] mProcDeaths = new int[20];
     
+    /**
+     * This is set if we had to do a delayed dexopt of an app before launching
+     * it, to increasing the ANR timeouts in that case.
+     */
+    boolean mDidDexOpt;
+    
     String mDebugApp = null;
     boolean mWaitForDebugger = false;
     boolean mDebugTransient = false;
@@ -852,6 +913,7 @@
     static final int SERVICE_ERROR_MSG = 18;
     static final int RESUME_TOP_ACTIVITY_MSG = 19;
     static final int PROC_START_TIMEOUT_MSG = 20;
+    static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
 
     AlertDialog mUidAlert;
 
@@ -910,6 +972,8 @@
                     d.show();
                     proc.anrDialog = d;
                 }
+                
+                ensureScreenEnabled();
             } break;
             case SHOW_FACTORY_ERROR_MSG: {
                 Dialog d = new FactoryErrorDialog(
@@ -952,6 +1016,12 @@
                 processNextBroadcast(true);
             } break;
             case BROADCAST_TIMEOUT_MSG: {
+                if (mDidDexOpt) {
+                    mDidDexOpt = false;
+                    Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
+                    mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT);
+                    return;
+                }
                 broadcastTimeout();
             } break;
             case PAUSE_TIMEOUT_MSG: {
@@ -962,9 +1032,16 @@
                 activityPaused(token, null, true);
             } break;
             case IDLE_TIMEOUT_MSG: {
-                IBinder token = (IBinder)msg.obj;
+                if (mDidDexOpt) {
+                    mDidDexOpt = false;
+                    Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+                    nmsg.obj = msg.obj;
+                    mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+                    return;
+                }
                 // We don't at this point know if the activity is fullscreen,
                 // so we need to be conservative and assume it isn't.
+                IBinder token = (IBinder)msg.obj;
                 Log.w(TAG, "Activity idle timeout for " + token);
                 activityIdleInternal(token, true);
             } break;
@@ -980,6 +1057,13 @@
                 activityIdle(token);
             } break;
             case SERVICE_TIMEOUT_MSG: {
+                if (mDidDexOpt) {
+                    mDidDexOpt = false;
+                    Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
+                    nmsg.obj = msg.obj;
+                    mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT);
+                    return;
+                }
                 serviceTimeout((ProcessRecord)msg.obj);
             } break;
             case UPDATE_TIME_ZONE: {
@@ -1016,6 +1100,12 @@
                 }
             } break;
             case LAUNCH_TIMEOUT_MSG: {
+                if (mDidDexOpt) {
+                    mDidDexOpt = false;
+                    Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+                    mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
+                    return;
+                }
                 synchronized (ActivityManagerService.this) {
                     if (mLaunchingActivity.isHeld()) {
                         Log.w(TAG, "Launch timeout has expired, giving up wake lock!");
@@ -1036,11 +1126,23 @@
                 }
             }
             case PROC_START_TIMEOUT_MSG: {
+                if (mDidDexOpt) {
+                    mDidDexOpt = false;
+                    Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+                    nmsg.obj = msg.obj;
+                    mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
+                    return;
+                }
                 ProcessRecord app = (ProcessRecord)msg.obj;
                 synchronized (ActivityManagerService.this) {
                     processStartTimedOutLocked(app);
                 }
             }
+            case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    doPendingActivityLaunchesLocked(true);
+                }
+            }
             }
         }
     };
@@ -1301,6 +1403,8 @@
             Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ"));
         SECONDARY_SERVER_ADJ =
             Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ"));
+        BACKUP_APP_ADJ =
+            Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ"));
         HOME_APP_ADJ =
             Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ"));
         HIDDEN_APP_MIN_ADJ =
@@ -1316,6 +1420,8 @@
             Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE;
         SECONDARY_SERVER_MEM =
             Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+        BACKUP_APP_MEM =
+            Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE;
         HOME_APP_MEM =
             Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE;
         HIDDEN_APP_MEM =
@@ -1382,7 +1488,7 @@
         synchronized (mProcessStatsThread) {
             final long now = SystemClock.uptimeMillis();
             boolean haveNewCpuStats = false;
-            
+
             if (MONITOR_CPU_USAGE &&
                     mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) {
                 mLastCpuTime = now;
@@ -1414,7 +1520,8 @@
                 }
             }
             
-            synchronized(mBatteryStatsService.getActiveStatistics()) {
+            final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
+            synchronized(bstats) {
                 synchronized(mPidsSelfLocked) {
                     if (haveNewCpuStats) {
                         if (mBatteryStatsService.isOnBattery()) {
@@ -1426,12 +1533,18 @@
                                 if (pr != null) {
                                     BatteryStatsImpl.Uid.Proc ps = pr.batteryStats;
                                     ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                } else {
+                                    BatteryStatsImpl.Uid.Proc ps =
+                                            bstats.getProcessStatsLocked(st.name, st.pid);
+                                    if (ps != null) {
+                                        ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                    }
                                 }
                             }
                         }
                     }
                 }
-        
+
                 if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
                     mLastWriteTime = now;
                     mBatteryStatsService.getActiveStatistics().writeLocked();
@@ -1495,6 +1608,18 @@
         return null;
     }
 
+    private final HistoryRecord topRunningNonDelayedActivityLocked(HistoryRecord notTop) {
+        int i = mHistory.size()-1;
+        while (i >= 0) {
+            HistoryRecord r = (HistoryRecord)mHistory.get(i);
+            if (!r.finishing && !r.delayedResume && r != notTop) {
+                return r;
+            }
+            i--;
+        }
+        return null;
+    }
+
     /**
      * This is a simplified version of topRunningActivityLocked that provides a number of
      * optional skip-over modes.  It is intended for use with the ActivityWatcher hook only.
@@ -1531,6 +1656,16 @@
         return proc;
     }
 
+    private void ensurePackageDexOpt(String packageName) {
+        IPackageManager pm = ActivityThread.getPackageManager();
+        try {
+            if (pm.performDexOpt(packageName)) {
+                mDidDexOpt = true;
+            }
+        } catch (RemoteException e) {
+        }
+    }
+    
     private boolean isNextTransitionForward() {
         int transit = mWindowManager.getPendingAppTransition();
         return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
@@ -1590,6 +1725,7 @@
             if (r.isHomeActivity) {
                 mHomeProcess = app;
             }
+            ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                     r.info, r.icicle, results, newIntents, !andResume,
                     isNextTransitionForward());
@@ -1640,6 +1776,12 @@
             r.stopped = true;
         }
 
+        // Launch the new version setup screen if needed.  We do this -after-
+        // launching the initial activity (that is, home), so that it can have
+        // a chance to initialize itself while in the background, making the
+        // switch back to it faster and look better.
+        startSetupActivityLocked();
+        
         return true;
     }
 
@@ -1995,6 +2137,25 @@
         if (prev != null) {
             prev.resumeKeyDispatchingLocked();
         }
+
+        if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
+            long diff = 0;
+            synchronized (mProcessStatsThread) {
+                diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
+            }
+            if (diff > 0) {
+                BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
+                synchronized (bsi) {
+                    BatteryStatsImpl.Uid.Proc ps =
+                            bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+                            prev.info.packageName);
+                    if (ps != null) {
+                        ps.addForegroundTimeLocked(diff);
+                    }
+                }
+            }
+        }
+        prev.cpuTimeAtResume = 0; // reset it
     }
 
     /**
@@ -2027,6 +2188,17 @@
         next.resumeKeyDispatchingLocked();
         ensureActivitiesVisibleLocked(null, 0);
         mWindowManager.executeAppTransition();
+
+        // Mark the point when the activity is resuming
+        // TODO: To be more accurate, the mark should be before the onCreate,
+        //       not after the onResume. But for subsequent starts, onResume is fine.
+        if (next.app != null) {
+            synchronized (mProcessStatsThread) {
+                next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
+            }
+        } else {
+            next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+        }
     }
 
     /**
@@ -2191,6 +2363,96 @@
         }
     }
 
+    private boolean startHomeActivityLocked() {
+        if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
+                && mTopAction == null) {
+            // We are running in factory test mode, but unable to find
+            // the factory test app, so just sit around displaying the
+            // error message and don't try to start anything.
+            return false;
+        }
+        Intent intent = new Intent(
+            mTopAction,
+            mTopData != null ? Uri.parse(mTopData) : null);
+        intent.setComponent(mTopComponent);
+        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+            intent.addCategory(Intent.CATEGORY_HOME);
+        }
+        ActivityInfo aInfo =
+            intent.resolveActivityInfo(mContext.getPackageManager(),
+                    STOCK_PM_FLAGS);
+        if (aInfo != null) {
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+            // Don't do this if the home app is currently being
+            // instrumented.
+            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
+                    aInfo.applicationInfo.uid);
+            if (app == null || app.instrumentationClass == null) {
+                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivityLocked(null, intent, null, null, 0, aInfo,
+                        null, null, 0, 0, 0, false, false);
+            }
+        }
+        
+        
+        return true;
+    }
+    
+    /**
+     * Starts the "new version setup screen" if appropriate.
+     */
+    private void startSetupActivityLocked() {
+        // Only do this once per boot.
+        if (mCheckedForSetup) {
+            return;
+        }
+        
+        // We will show this screen if the current one is a different
+        // version than the last one shown, and we are not running in
+        // low-level factory test mode.
+        final ContentResolver resolver = mContext.getContentResolver();
+        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL &&
+                Settings.Secure.getInt(resolver,
+                        Settings.Secure.DEVICE_PROVISIONED, 0) != 0) {
+            mCheckedForSetup = true;
+            
+            // See if we should be showing the platform update setup UI.
+            Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
+            List<ResolveInfo> ris = mSelf.mContext.getPackageManager()
+                    .queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            
+            // We don't allow third party apps to replace this.
+            ResolveInfo ri = null;
+            for (int i=0; ris != null && i<ris.size(); i++) {
+                if ((ris.get(i).activityInfo.applicationInfo.flags
+                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    ri = ris.get(i);
+                    break;
+                }
+            }
+            
+            if (ri != null) {
+                String vers = ri.activityInfo.metaData != null
+                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
+                        : null;
+                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
+                    vers = ri.activityInfo.applicationInfo.metaData.getString(
+                            Intent.METADATA_SETUP_VERSION);
+                }
+                String lastVers = Settings.Secure.getString(
+                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
+                if (vers != null && !vers.equals(lastVers)) {
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setComponent(new ComponentName(
+                            ri.activityInfo.packageName, ri.activityInfo.name));
+                    startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+                            null, null, 0, 0, 0, false, false);
+                }
+            }
+        }
+    }
+    
     /**
      * Ensure that the top activity in the stack is resumed.
      *
@@ -2212,39 +2474,11 @@
         if (next == null) {
             // There are no more activities!  Let's just start up the
             // Launcher...
-            if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
-                    && mTopAction == null) {
-                // We are running in factory test mode, but unable to find
-                // the factory test app, so just sit around displaying the
-                // error message and don't try to start anything.
-                return false;
-            }
-            Intent intent = new Intent(
-                mTopAction,
-                mTopData != null ? Uri.parse(mTopData) : null);
-            intent.setComponent(mTopComponent);
-            if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-                intent.addCategory(Intent.CATEGORY_HOME);
-            }
-            ActivityInfo aInfo =
-                intent.resolveActivityInfo(mContext.getPackageManager(),
-                        STOCK_PM_FLAGS);
-            if (aInfo != null) {
-                intent.setComponent(new ComponentName(
-                        aInfo.applicationInfo.packageName, aInfo.name));
-                // Don't do this if the home app is currently being
-                // instrumented.
-                ProcessRecord app = getProcessRecordLocked(aInfo.processName,
-                        aInfo.applicationInfo.uid);
-                if (app == null || app.instrumentationClass == null) {
-                    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                    startActivityLocked(null, intent, null, null, 0, aInfo,
-                            null, null, 0, 0, 0, false, false);
-                }
-            }
-            return true;
+            return startHomeActivityLocked();
         }
 
+        next.delayedResume = false;
+        
         // If the top activity is the resumed one, nothing to do.
         if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
             // Make sure we have executed any pending transitions, since there
@@ -2471,7 +2705,8 @@
         return true;
     }
 
-    private final void startActivityLocked(HistoryRecord r, boolean newTask) {
+    private final void startActivityLocked(HistoryRecord r, boolean newTask,
+            boolean doResume) {
         final int NH = mHistory.size();
 
         int addPos = -1;
@@ -2558,7 +2793,7 @@
                 if ((r.intent.getFlags()
                         &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                     resetTaskIfNeededLocked(r, r);
-                    doShow = topRunningActivityLocked(null) == r;
+                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                 }
             }
             if (SHOW_APP_STARTING_ICON && doShow) {
@@ -2588,13 +2823,15 @@
             mWindowManager.validateAppTokens(mHistory);
         }
 
-        resumeTopActivityLocked(null);
+        if (doResume) {
+            resumeTopActivityLocked(null);
+        }
     }
 
     /**
      * Perform clear operation as requested by
-     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: assuming the top task on the
-     * stack is the one that the new activity is being launched in, look for
+     * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+     * stack to the given task, then look for
      * an instance of that activity in the stack and, if found, finish all
      * activities on top of it and return the instance.
      *
@@ -2602,9 +2839,21 @@
      * @return Returns the old activity that should be continue to be used,
      * or null if none was found.
      */
-    private final HistoryRecord performClearTopTaskLocked(int taskId,
+    private final HistoryRecord performClearTaskLocked(int taskId,
             HistoryRecord newR, boolean doClear) {
         int i = mHistory.size();
+        
+        // First find the requested task.
+        while (i > 0) {
+            i--;
+            HistoryRecord r = (HistoryRecord)mHistory.get(i);
+            if (r.task.taskId == taskId) {
+                i++;
+                break;
+            }
+        }
+        
+        // Now clear it.
         while (i > 0) {
             i--;
             HistoryRecord r = (HistoryRecord)mHistory.get(i);
@@ -2636,7 +2885,7 @@
                 // instance of the activity so a new fresh one can be started.
                 if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {
                     if (!ret.finishing) {
-                        int index = indexOfTokenLocked(ret, false);
+                        int index = indexOfTokenLocked(ret);
                         if (index >= 0) {
                             finishActivityLocked(ret, 0, Activity.RESULT_CANCELED,
                                     null, "clear");
@@ -2729,7 +2978,7 @@
         HistoryRecord sourceRecord = null;
         HistoryRecord resultRecord = null;
         if (resultTo != null) {
-            int index = indexOfTokenLocked(resultTo, false);
+            int index = indexOfTokenLocked(resultTo);
             if (DEBUG_RESULTS) Log.v(
                 TAG, "Sending result to " + resultTo + " (index " + index + ")");
             if (index >= 0) {
@@ -2840,15 +3089,75 @@
                 intent, resolvedType, aInfo, mConfiguration,
                 resultRecord, resultWho, requestCode, componentSpecified);
 
-        HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
-                != 0 ? r : null;
-
+        if (mResumedActivity == null
+                || mResumedActivity.info.applicationInfo.uid != callingUid) {
+            if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+                PendingActivityLaunch pal = new PendingActivityLaunch();
+                pal.r = r;
+                pal.sourceRecord = sourceRecord;
+                pal.grantedUriPermissions = grantedUriPermissions;
+                pal.grantedMode = grantedMode;
+                pal.onlyIfNeeded = onlyIfNeeded;
+                mPendingActivityLaunches.add(pal);
+                return START_SWITCHES_CANCELED;
+            }
+        }
+        
+        if (mDidAppSwitch) {
+            // This is the second allowed switch since we stopped switches,
+            // so now just generally allow switches.  Use case: user presses
+            // home (switches disabled, switch to home, mDidAppSwitch now true);
+            // user taps a home icon (coming from home so allowed, we hit here
+            // and now allow anyone to switch again).
+            mAppSwitchesAllowedTime = 0;
+        } else {
+            mDidAppSwitch = true;
+        }
+     
+        doPendingActivityLaunchesLocked(false);
+        
+        return startActivityUncheckedLocked(r, sourceRecord,
+                grantedUriPermissions, grantedMode, onlyIfNeeded, true);
+    }
+  
+    private final void doPendingActivityLaunchesLocked(boolean doResume) {
+        final int N = mPendingActivityLaunches.size();
+        if (N <= 0) {
+            return;
+        }
+        for (int i=0; i<N; i++) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
+            startActivityUncheckedLocked(pal.r, pal.sourceRecord,
+                    pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded,
+                    doResume && i == (N-1));
+        }
+        mPendingActivityLaunches.clear();
+    }
+    
+    private final int startActivityUncheckedLocked(HistoryRecord r,
+            HistoryRecord sourceRecord, Uri[] grantedUriPermissions,
+            int grantedMode, boolean onlyIfNeeded, boolean doResume) {
+        final Intent intent = r.intent;
+        final int callingUid = r.launchedFromUid;
+        
+        int launchFlags = intent.getFlags();
+        
         // We'll invoke onUserLeaving before onPause only if the launching
         // activity did not explicitly state that this is an automated launch.
         mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
         if (DEBUG_USER_LEAVING) Log.v(TAG,
                 "startActivity() => mUserLeaving=" + mUserLeaving);
         
+        // If the caller has asked not to resume at this point, we make note
+        // of this in the record so that we can skip it when trying to find
+        // the top running activity.
+        if (!doResume) {
+            r.delayedResume = true;
+        }
+        
+        HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
+                != 0 ? r : null;
+
         // If the onlyIfNeeded flag is set, then we can do this if the activity
         // being launched is the same as the one making the call...  or, as
         // a special case, if we do not know the caller then we count the
@@ -2856,7 +3165,7 @@
         if (onlyIfNeeded) {
             HistoryRecord checkedCaller = sourceRecord;
             if (checkedCaller == null) {
-                checkedCaller = topRunningActivityLocked(notTop);
+                checkedCaller = topRunningNonDelayedActivityLocked(notTop);
             }
             if (!checkedCaller.realActivity.equals(r.realActivity)) {
                 // Caller is not the same as launcher, so always needed.
@@ -2894,7 +3203,7 @@
             launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
         }
 
-        if (resultRecord != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new
             // task...  yet the caller has requested a result back.  Well, that
             // is pretty messed up, so instead immediately send back a cancel
@@ -2902,10 +3211,9 @@
             // dependency on its originator.
             Log.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
             sendActivityResultLocked(-1,
-                resultRecord, resultWho, requestCode,
+                    r.resultTo, r.resultWho, r.requestCode,
                 Activity.RESULT_CANCELED, null);
             r.resultTo = null;
-            resultRecord = null;
         }
 
         boolean addingToTask = false;
@@ -2916,7 +3224,7 @@
             // If bring to front is requested, and no result is requested, and
             // we can find a task that was started with this same
             // component, then instead of launching bring that one to the front.
-            if (resultRecord == null) {
+            if (r.resultTo == null) {
                 // See if there is a task to bring to the front.  If this is
                 // a SINGLE_INSTANCE activity, there can be one and only one
                 // instance of it in the history, and it is always in its own
@@ -2938,7 +3246,7 @@
                     // to have the same behavior as if a new instance was
                     // being started, which means not bringing it to the front
                     // if the caller is not itself in the front.
-                    HistoryRecord curTop = topRunningActivityLocked(notTop);
+                    HistoryRecord curTop = topRunningNonDelayedActivityLocked(notTop);
                     if (curTop.task != taskTop.task) {
                         r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                         boolean callerAtFront = sourceRecord == null
@@ -2959,7 +3267,9 @@
                         // the client said not to do anything if that
                         // is the case, so this is it!  And for paranoia, make
                         // sure we have correctly resumed the top activity.
-                        resumeTopActivityLocked(null);
+                        if (doResume) {
+                            resumeTopActivityLocked(null);
+                        }
                         return START_RETURN_INTENT_TO_CALLER;
                     }
                     if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
@@ -2969,7 +3279,7 @@
                         // from the task up to the one being started.  In most
                         // cases this means we are resetting the task to its
                         // initial state.
-                        HistoryRecord top = performClearTopTaskLocked(
+                        HistoryRecord top = performClearTaskLocked(
                                 taskTop.task.taskId, r, true);
                         if (top != null) {
                             if (top.frontOfTask) {
@@ -3035,7 +3345,9 @@
                         // We didn't do anything...  but it was needed (a.k.a., client
                         // don't use that intent!)  And for paranoia, make
                         // sure we have correctly resumed the top activity.
-                        resumeTopActivityLocked(null);
+                        if (doResume) {
+                            resumeTopActivityLocked(null);
+                        }
                         return START_TASK_TO_FRONT;
                     }
                 }
@@ -3052,8 +3364,8 @@
             // If the activity being launched is the same as the one currently
             // at the top, then we need to check if it should only be launched
             // once.
-            HistoryRecord top = topRunningActivityLocked(notTop);
-            if (top != null && resultRecord == null) {
+            HistoryRecord top = topRunningNonDelayedActivityLocked(notTop);
+            if (top != null && r.resultTo == null) {
                 if (top.realActivity.equals(r.realActivity)) {
                     if (top.app != null && top.app.thread != null) {
                         if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
@@ -3062,7 +3374,9 @@
                             logStartActivity(LOG_AM_NEW_INTENT, top, top.task);
                             // For paranoia, make sure we have correctly
                             // resumed the top activity.
-                            resumeTopActivityLocked(null);
+                            if (doResume) {
+                                resumeTopActivityLocked(null);
+                            }
                             if (onlyIfNeeded) {
                                 // We don't need to start a new activity, and
                                 // the client said not to do anything if that
@@ -3077,9 +3391,9 @@
             }
 
         } else {
-            if (resultRecord != null) {
+            if (r.resultTo != null) {
                 sendActivityResultLocked(-1,
-                    resultRecord, resultWho, requestCode,
+                        r.resultTo, r.resultWho, r.requestCode,
                     Activity.RESULT_CANCELED, null);
             }
             return START_CLASS_NOT_FOUND;
@@ -3088,7 +3402,7 @@
         boolean newTask = false;
 
         // Should this be considered a new task?
-        if (resultRecord == null && !addingToTask
+        if (r.resultTo == null && !addingToTask
                 && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
             // todo: should do better management of integers.
             mCurTask++;
@@ -3108,14 +3422,16 @@
                 // In this case, we are adding the activity to an existing
                 // task, but the caller has asked to clear that task if the
                 // activity is already running.
-                HistoryRecord top = performClearTopTaskLocked(
+                HistoryRecord top = performClearTaskLocked(
                         sourceRecord.task.taskId, r, true);
                 if (top != null) {
                     logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
                     deliverNewIntentLocked(top, r.intent);
                     // For paranoia, make sure we have correctly
                     // resumed the top activity.
-                    resumeTopActivityLocked(null);
+                    if (doResume) {
+                        resumeTopActivityLocked(null);
+                    }
                     return START_DELIVERED_TO_TOP;
                 }
             } else if (!addingToTask &&
@@ -3128,7 +3444,9 @@
                     HistoryRecord top = moveActivityToFrontLocked(where);
                     logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
                     deliverNewIntentLocked(top, r.intent);
-                    resumeTopActivityLocked(null);
+                    if (doResume) {
+                        resumeTopActivityLocked(null);
+                    }
                     return START_DELIVERED_TO_TOP;
                 }
             }
@@ -3157,7 +3475,7 @@
             EventLog.writeEvent(LOG_AM_CREATE_TASK, r.task.taskId);
         }
         logStartActivity(LOG_AM_CREATE_ACTIVITY, r, r.task);
-        startActivityLocked(r, newTask);
+        startActivityLocked(r, newTask, doResume);
         return START_SUCCESS;
     }
 
@@ -3226,7 +3544,7 @@
         }
 
         synchronized (this) {
-            int index = indexOfTokenLocked(callingActivity, false);
+            int index = indexOfTokenLocked(callingActivity);
             if (index < 0) {
                 return false;
             }
@@ -3376,7 +3694,7 @@
     public void setRequestedOrientation(IBinder token,
             int requestedOrientation) {
         synchronized (this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
@@ -3398,7 +3716,7 @@
 
     public int getRequestedOrientation(IBinder token) {
         synchronized (this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index < 0) {
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             }
@@ -3454,7 +3772,7 @@
             TAG, "Finishing activity: token=" + token
             + ", result=" + resultCode + ", data=" + resultData);
 
-        int index = indexOfTokenLocked(token, false);
+        int index = indexOfTokenLocked(token);
         if (index < 0) {
             return false;
         }
@@ -3578,7 +3896,7 @@
 
     private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r,
             int mode) {
-        final int index = indexOfTokenLocked(r, false);
+        final int index = indexOfTokenLocked(r);
         if (index < 0) {
             return null;
         }
@@ -3703,7 +4021,7 @@
     public final void finishSubActivity(IBinder token, String resultWho,
             int requestCode) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
@@ -4253,7 +4571,7 @@
         }
 
         synchronized(this) {
-            int index = indexOfTokenLocked(token, true);
+            int index = indexOfTokenLocked(token);
             if (index < 0) {
                 return;
             }
@@ -4523,6 +4841,16 @@
                 mPendingBroadcast = null;
                 scheduleBroadcastsLocked();
             }
+            if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
+                Log.w(TAG, "Unattached app died before backup, skipping");
+                try {
+                    IBackupManager bm = IBackupManager.Stub.asInterface(
+                            ServiceManager.getService(Context.BACKUP_SERVICE));
+                    bm.agentDisconnected(app.info.packageName);
+                } catch (RemoteException e) {
+                    // Can't happen; the backup manager is local
+                }
+            }
         } else {
             Log.w(TAG, "Spurious process start timeout - pid not known for " + app);
         }
@@ -4587,6 +4915,7 @@
         
         app.thread = thread;
         app.curAdj = app.setAdj = -100;
+        app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
         app.forcingToForeground = null;
         app.foregroundServices = false;
         app.debugging = false;
@@ -4610,11 +4939,23 @@
                     mWaitForDebugger = mOrigWaitForDebugger;
                 }
             }
+            // If the app is being launched for restore or full backup, set it up specially
+            boolean isRestrictedBackupMode = false;
+            if (mBackupTarget != null && mBackupAppName.equals(processName)) {
+                isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
+                        || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
+            }
+            ensurePackageDexOpt(app.instrumentationInfo != null
+                    ? app.instrumentationInfo.packageName
+                    : app.info.packageName);
+            if (app.instrumentationClass != null) {
+                ensurePackageDexOpt(app.instrumentationClass.getPackageName());
+            }
             thread.bindApplication(processName, app.instrumentationInfo != null
                     ? app.instrumentationInfo : app.info, providers,
                     app.instrumentationClass, app.instrumentationProfileFile,
                     app.instrumentationArguments, app.instrumentationWatcher, testMode, 
-                    mConfiguration, getCommonServicesLocked());
+                    isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());
             updateLRUListLocked(app, false);
             app.lastRequestedGc = SystemClock.uptimeMillis();
         } catch (Exception e) {
@@ -4695,6 +5036,18 @@
             }
         }
 
+        // Check whether the next backup agent is in this process...
+        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
+            if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app);
+            ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
+            try {
+                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
+            } catch (Exception e) {
+                Log.w(TAG, "Exception scheduling backup agent creation: ");
+                e.printStackTrace();
+            }
+        }
+
         if (badApp) {
             // todo: Also need to kill application to deal with all
             // kinds of exceptions.
@@ -4790,7 +5143,7 @@
             }
 
             // Get the activity record.
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 HistoryRecord r = (HistoryRecord)mHistory.get(index);
 
@@ -4911,6 +5264,20 @@
         }
     }
 
+    final void ensureScreenEnabled() {
+        boolean enableScreen;
+        synchronized (this) {
+            enableScreen = !mBooted;
+            mBooted = true;
+        }
+
+        if (enableScreen) {
+            EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
+                SystemClock.uptimeMillis());
+            enableScreenAfterBoot();
+        }
+    }
+    
     public final void activityPaused(IBinder token, Bundle icicle) {
         // Refuse possible leaked file descriptors
         if (icicle != null && icicle.hasFileDescriptors()) {
@@ -4930,7 +5297,7 @@
         HistoryRecord r = null;
 
         synchronized (this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 r = (HistoryRecord)mHistory.get(index);
                 if (!timeout) {
@@ -4961,7 +5328,7 @@
         final long origId = Binder.clearCallingIdentity();
 
         synchronized (this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 r = (HistoryRecord)mHistory.get(index);
                 r.thumbnail = thumbnail;
@@ -4991,7 +5358,7 @@
         synchronized (this) {
             mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
             
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 HistoryRecord r = (HistoryRecord)mHistory.get(index);
                 if (r.state == ActivityState.DESTROYING) {
@@ -5018,7 +5385,7 @@
     }
 
     private HistoryRecord getCallingRecordLocked(IBinder token) {
-        int index = indexOfTokenLocked(token, true);
+        int index = indexOfTokenLocked(token);
         if (index >= 0) {
             HistoryRecord r = (HistoryRecord)mHistory.get(index);
             if (r != null) {
@@ -5030,7 +5397,7 @@
 
     public ComponentName getActivityClassForToken(IBinder token) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 HistoryRecord r = (HistoryRecord)mHistory.get(index);
                 return r.intent.getComponent();
@@ -5041,7 +5408,7 @@
 
     public String getPackageForToken(IBinder token) {
         synchronized(this) {
-            int index = indexOfTokenLocked(token, false);
+            int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 HistoryRecord r = (HistoryRecord)mHistory.get(index);
                 return r.packageName;
@@ -5080,7 +5447,7 @@
             }
             HistoryRecord activity = null;
             if (type == INTENT_SENDER_ACTIVITY_RESULT) {
-                int index = indexOfTokenLocked(token, false);
+                int index = indexOfTokenLocked(token);
                 if (index < 0) {
                     return null;
                 }
@@ -6251,6 +6618,10 @@
                 "moveTaskToFront()");
 
         synchronized(this) {
+            if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+                    Binder.getCallingUid(), "Task to front")) {
+                return;
+            }
             final long origId = Binder.clearCallingIdentity();
             try {
                 int N = mRecentTasks.size();
@@ -6335,6 +6706,12 @@
                 "moveTaskToBack()");
 
         synchronized(this) {
+            if (mResumedActivity != null && mResumedActivity.task.taskId == task) {
+                if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+                        Binder.getCallingUid(), "Task to back")) {
+                    return;
+                }
+            }
             final long origId = Binder.clearCallingIdentity();
             moveTaskToBackLocked(task);
             Binder.restoreCallingIdentity(origId);
@@ -6438,6 +6815,10 @@
                 "moveTaskBackwards()");
 
         synchronized(this) {
+            if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+                    Binder.getCallingUid(), "Task backwards")) {
+                return;
+            }
             final long origId = Binder.clearCallingIdentity();
             moveTaskBackwardsLocked(task);
             Binder.restoreCallingIdentity(origId);
@@ -6587,7 +6968,7 @@
 
         synchronized(this) {
             if (r == null) {
-                int index = indexOfTokenLocked(token, false);
+                int index = indexOfTokenLocked(token);
                 if (index < 0) {
                     return;
                 }
@@ -6670,6 +7051,7 @@
                 }
                 app.pubProviders.put(cpi.name, cpr);
                 app.addPackage(cpi.applicationInfo.packageName);
+                ensurePackageDexOpt(cpi.applicationInfo.packageName);
             }
         }
         return providers;
@@ -7179,6 +7561,55 @@
         }
     }
 
+    public void stopAppSwitches() {
+        if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.STOP_APP_SWITCHES);
+        }
+        
+        synchronized(this) {
+            mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
+                    + APP_SWITCH_DELAY_TIME;
+            mDidAppSwitch = false;
+            mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+            Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+            mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
+        }
+    }
+    
+    public void resumeAppSwitches() {
+        if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.STOP_APP_SWITCHES);
+        }
+        
+        synchronized(this) {
+            // Note that we don't execute any pending app switches... we will
+            // let those wait until either the timeout, or the next start
+            // activity request.
+            mAppSwitchesAllowedTime = 0;
+        }
+    }
+    
+    boolean checkAppSwitchAllowedLocked(int callingPid, int callingUid,
+            String name) {
+        if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
+            return true;
+        }
+            
+        final int perm = checkComponentPermission(
+                android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
+                callingUid, -1);
+        if (perm == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
+        
+        Log.w(TAG, name + " request from " + callingUid + " stopped");
+        return false;
+    }
+    
     public void setDebugApp(String packageName, boolean waitForDebugger,
             boolean persistent) {
         enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
@@ -7595,6 +8026,31 @@
         return handleAppCrashLocked(app);
     }
 
+    private ComponentName getErrorReportReceiver(ProcessRecord app) {
+        IPackageManager pm = ActivityThread.getPackageManager();
+        try {
+            // was an installer package name specified when this app was
+            // installed?
+            String installerPackageName = pm.getInstallerPackageName(app.info.packageName);
+            if (installerPackageName == null) {
+                return null;
+            }
+
+            // is there an Activity in this package that handles ACTION_APP_ERROR?
+            Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+            intent.setPackage(installerPackageName);
+            ResolveInfo info = pm.resolveIntent(intent, null, 0);
+            if (info == null || info.activityInfo == null) {
+                return null;
+            }
+
+            return new ComponentName(installerPackageName, info.activityInfo.name);
+        } catch (RemoteException e) {
+            // will return null and no error report will be delivered
+        }
+        return null;
+    }
+
     void makeAppNotRespondingLocked(ProcessRecord app,
             String tag, String shortMsg, String longMsg, byte[] crashData) {
         app.notResponding = true;
@@ -7713,6 +8169,7 @@
     }
 
     void startAppProblemLocked(ProcessRecord app) {
+        app.errorReportReceiver = getErrorReportReceiver(app);
         skipCurrentReceiverLocked(app);
     }
 
@@ -7745,7 +8202,6 @@
     public int handleApplicationError(IBinder app, int flags,
             String tag, String shortMsg, String longMsg, byte[] crashData) {
         AppErrorResult result = new AppErrorResult();
-
         ProcessRecord r = null;
         synchronized (this) {
             if (app != null) {
@@ -7834,16 +8290,103 @@
 
         int res = result.get();
 
+        Intent appErrorIntent = null;
         synchronized (this) {
             if (r != null) {
                 mProcessCrashTimes.put(r.info.processName, r.info.uid,
                         SystemClock.uptimeMillis());
             }
+            if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
+                appErrorIntent = createAppErrorIntentLocked(r);
+                res = AppErrorDialog.FORCE_QUIT;
+            }
+        }
+
+        if (appErrorIntent != null) {
+            try {
+                mContext.startActivity(appErrorIntent);
+            } catch (ActivityNotFoundException e) {
+                Log.w(TAG, "bug report receiver dissappeared", e);
+            }
         }
 
         return res;
     }
     
+    Intent createAppErrorIntentLocked(ProcessRecord r) {
+        ApplicationErrorReport report = createAppErrorReportLocked(r);
+        if (report == null) {
+            return null;
+        }
+        Intent result = new Intent(Intent.ACTION_APP_ERROR);
+        result.setComponent(r.errorReportReceiver);
+        result.putExtra(Intent.EXTRA_BUG_REPORT, report);
+        result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return result;
+    }
+
+    ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) {
+        if (r.errorReportReceiver == null) {
+            return null;
+        }
+
+        if (!r.crashing && !r.notResponding) {
+            return null;
+        }
+
+        try {
+            ApplicationErrorReport report = new ApplicationErrorReport();
+            report.packageName = r.info.packageName;
+            report.installerPackageName = r.errorReportReceiver.getPackageName();
+            report.processName = r.processName;
+
+            if (r.crashing) {
+                report.type = ApplicationErrorReport.TYPE_CRASH;
+                report.crashInfo = new ApplicationErrorReport.CrashInfo();
+
+                ByteArrayInputStream byteStream = new ByteArrayInputStream(
+                        r.crashingReport.crashData);
+                DataInputStream dataStream = new DataInputStream(byteStream);
+                CrashData crashData = new CrashData(dataStream);
+                ThrowableData throwData = crashData.getThrowableData();
+
+                report.time = crashData.getTime();
+                report.crashInfo.stackTrace = throwData.toString();
+
+                // Extract the source of the exception, useful for report
+                // clustering. Also extract the "deepest" non-null exception
+                // message.
+                String exceptionMessage = throwData.getMessage();
+                while (throwData.getCause() != null) {
+                    throwData = throwData.getCause();
+                    String msg = throwData.getMessage();
+                    if (msg != null && msg.length() > 0) {
+                       exceptionMessage = msg;
+                    }
+                }
+                StackTraceElementData trace = throwData.getStackTrace()[0];
+                report.crashInfo.exceptionMessage = exceptionMessage;
+                report.crashInfo.exceptionClassName = throwData.getType();
+                report.crashInfo.throwFileName = trace.getFileName();
+                report.crashInfo.throwClassName = trace.getClassName();
+                report.crashInfo.throwMethodName = trace.getMethodName();
+            } else if (r.notResponding) {
+                report.type = ApplicationErrorReport.TYPE_ANR;
+                report.anrInfo = new ApplicationErrorReport.AnrInfo();
+
+                report.anrInfo.activity = r.notRespondingReport.tag;
+                report.anrInfo.cause = r.notRespondingReport.shortMsg;
+                report.anrInfo.info = r.notRespondingReport.longMsg;
+            }
+
+            return report;
+        } catch (IOException e) {
+            // we don't send it
+        }
+
+        return null;
+    }
+
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
         // assume our apps are happy - lazy create the list
         List<ActivityManager.ProcessErrorStateInfo> errList = null;
@@ -8475,9 +9018,9 @@
                       + " #" + i + ":");
                 r.dump(pw, prefix + "  ");
             } else if (inclOomAdj) {
-                pw.println(String.format("%s%s #%2d: oom_adj=%3d %s",
+                pw.println(String.format("%s%s #%2d: adj=%3d/%d %s",
                         prefix, (r.persistent ? persistentLabel : normalLabel),
-                        i, r.setAdj, r.toString()));
+                        i, r.setAdj, r.setSchedGroup, r.toString()));
             } else {
                 pw.println(String.format("%s%s #%2d: %s",
                         prefix, (r.persistent ? persistentLabel : normalLabel),
@@ -8540,7 +9083,7 @@
         return false;
     }
 
-    private final int indexOfTokenLocked(IBinder token, boolean required) {
+    private final int indexOfTokenLocked(IBinder token) {
         int count = mHistory.size();
 
         // convert the token to an entry in the history.
@@ -8554,19 +9097,10 @@
                 break;
             }
         }
-        if (index < 0 && required) {
-            RuntimeInit.crash(TAG, new InvalidTokenException(token));
-        }
 
         return index;
     }
 
-    static class InvalidTokenException extends Exception {
-        InvalidTokenException(IBinder token) {
-            super("Bad activity token: " + token);
-        }
-    }
-
     private final void killServicesLocked(ProcessRecord app,
             boolean allowRestart) {
         // Report disconnected services.
@@ -8790,6 +9324,18 @@
             app.receivers.clear();
         }
         
+        // If the app is undergoing backup, tell the backup manager about it
+        if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
+            if (DEBUG_BACKUP) Log.d(TAG, "App " + mBackupTarget.appInfo + " died during backup");
+            try {
+                IBackupManager bm = IBackupManager.Stub.asInterface(
+                        ServiceManager.getService(Context.BACKUP_SERVICE));
+                bm.agentDisconnected(app.info.packageName);
+            } catch (RemoteException e) {
+                // can't happen; backup manager is local
+            }
+        }
+
         // If the caller is restarting this app, then leave it in its
         // current lists and let the caller take care of it.
         if (restarting) {
@@ -9130,6 +9676,7 @@
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startLaunchedLocked();
             }
+            ensurePackageDexOpt(r.serviceInfo.packageName);
             app.thread.scheduleCreateService(r, r.serviceInfo);
             created = true;
         } finally {
@@ -9579,7 +10126,7 @@
 
             HistoryRecord activity = null;
             if (token != null) {
-                int aindex = indexOfTokenLocked(token, false);
+                int aindex = indexOfTokenLocked(token);
                 if (aindex < 0) {
                     Log.w(TAG, "Binding with unknown activity: " + token);
                     return 0;
@@ -9906,6 +10453,128 @@
     }
     
     // =========================================================
+    // BACKUP AND RESTORE
+    // =========================================================
+    
+    // Cause the target app to be launched if necessary and its backup agent
+    // instantiated.  The backup agent will invoke backupAgentCreated() on the
+    // activity manager to announce its creation.
+    public boolean bindBackupAgent(ApplicationInfo app, int backupMode) {
+        if (DEBUG_BACKUP) Log.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode);
+        enforceCallingPermission("android.permission.BACKUP", "startBackupAgent");
+
+        synchronized(this) {
+            // !!! TODO: currently no check here that we're already bound
+            BatteryStatsImpl.Uid.Pkg.Serv ss = null;
+            BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+            synchronized (stats) {
+                ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name);
+            }
+
+            BackupRecord r = new BackupRecord(ss, app, backupMode);
+            ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
+            // startProcessLocked() returns existing proc's record if it's already running
+            ProcessRecord proc = startProcessLocked(app.processName, app,
+                    false, 0, "backup", hostingName);
+            if (proc == null) {
+                Log.e(TAG, "Unable to start backup agent process " + r);
+                return false;
+            }
+
+            r.app = proc;
+            mBackupTarget = r;
+            mBackupAppName = app.packageName;
+
+            // Try not to kill the process during backup
+            updateOomAdjLocked(proc);
+
+            // If the process is already attached, schedule the creation of the backup agent now.
+            // If it is not yet live, this will be done when it attaches to the framework.
+            if (proc.thread != null) {
+                if (DEBUG_BACKUP) Log.v(TAG, "Agent proc already running: " + proc);
+                try {
+                    proc.thread.scheduleCreateBackupAgent(app, backupMode);
+                } catch (RemoteException e) {
+                    // !!! TODO: notify the backup manager that we crashed, or rely on
+                    // death notices, or...?
+                }
+            } else {
+                if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach");
+            }
+            // Invariants: at this point, the target app process exists and the application
+            // is either already running or in the process of coming up.  mBackupTarget and
+            // mBackupAppName describe the app, so that when it binds back to the AM we
+            // know that it's scheduled for a backup-agent operation.
+        }
+        
+        return true;
+    }
+
+    // A backup agent has just come up                    
+    public void backupAgentCreated(String agentPackageName, IBinder agent) {
+        if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName
+                + " = " + agent);
+
+        synchronized(this) {
+            if (!agentPackageName.equals(mBackupAppName)) {
+                Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
+                return;
+            }
+
+            long oldIdent = Binder.clearCallingIdentity();
+            try {
+                IBackupManager bm = IBackupManager.Stub.asInterface(
+                        ServiceManager.getService(Context.BACKUP_SERVICE));
+                bm.agentConnected(agentPackageName, agent);
+            } catch (RemoteException e) {
+                // can't happen; the backup manager service is local
+            } catch (Exception e) {
+                Log.w(TAG, "Exception trying to deliver BackupAgent binding: ");
+                e.printStackTrace();
+            } finally {
+                Binder.restoreCallingIdentity(oldIdent);
+            }
+        }
+    }
+
+    // done with this agent
+    public void unbindBackupAgent(ApplicationInfo appInfo) {
+        if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo);
+        if (appInfo == null) {
+            Log.w(TAG, "unbind backup agent for null app");
+            return;
+        }
+
+        synchronized(this) {
+            if (mBackupAppName == null) {
+                Log.w(TAG, "Unbinding backup agent with no active backup");
+                return;
+            }
+
+            if (!mBackupAppName.equals(appInfo.packageName)) {
+                Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
+                return;
+            }
+
+            ProcessRecord proc = mBackupTarget.app;
+            mBackupTarget = null;
+            mBackupAppName = null;
+
+            // Not backing this app up any more; reset its OOM adjustment
+            updateOomAdjLocked(proc);
+
+            // If the app crashed during backup, 'thread' will be null here
+            if (proc.thread != null) {
+                try {
+                    proc.thread.scheduleDestroyBackupAgent(appInfo);
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception when unbinding backup agent:");
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+    // =========================================================
     // BROADCASTS
     // =========================================================
 
@@ -10078,7 +10747,7 @@
             boolean ordered, boolean sticky, int callingPid, int callingUid) {
         intent = new Intent(intent);
 
-        if (DEBUG_BROADCAST) Log.v(
+        if (DEBUG_BROADCAST_LIGHT) Log.v(
             TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
             + " ordered=" + ordered);
         if ((resultTo != null) && !ordered) {
@@ -10114,6 +10783,10 @@
                         if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
                             uninstallPackageLocked(ssp,
                                     intent.getIntExtra(Intent.EXTRA_UID, -1), false);
+                            AttributeCache ac = AttributeCache.instance();
+                            if (ac != null) {
+                                ac.removePackage(ssp);
+                            }
                         }
                     }
                 }
@@ -10176,8 +10849,6 @@
             }
         }
 
-        final ContentResolver resolver = mContext.getContentResolver();
-
         // Figure out who all will receive this broadcast.
         List receivers = null;
         List<BroadcastFilter> registeredReceivers = null;
@@ -10200,8 +10871,7 @@
                         ActivityThread.getPackageManager().queryIntentReceivers(
                                 intent, resolvedType, STOCK_PM_FLAGS);
                 }
-                registeredReceivers = mReceiverResolver.queryIntent(resolver,
-                        intent, resolvedType, false);
+                registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);
             }
         } catch (RemoteException ex) {
             // pm is in same process, this will never happen.
@@ -10573,9 +11243,10 @@
 
         boolean started = false;
         try {
-            if (DEBUG_BROADCAST) Log.v(TAG,
+            if (DEBUG_BROADCAST_LIGHT) Log.v(TAG,
                     "Delivering to component " + r.curComponent
                     + ": " + r);
+            ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     r.resultCode, r.resultData, r.resultExtras, r.ordered);
             started = true;
@@ -10643,12 +11314,22 @@
                 r.curFilter = filter;
                 filter.receiverList.curBroadcast = r;
                 r.state = BroadcastRecord.CALL_IN_RECEIVE;
+                if (filter.receiverList.app != null) {
+                    // Bump hosting application to no longer be in background
+                    // scheduling class.  Note that we can't do that if there
+                    // isn't an app...  but we can only be in that case for
+                    // things that directly call the IActivityManager API, which
+                    // are already core system stuff so don't matter for this.
+                    r.curApp = filter.receiverList.app;
+                    filter.receiverList.app.curReceiver = r;
+                    updateOomAdjLocked();
+                }
             }
             try {
-                if (DEBUG_BROADCAST) {
+                if (DEBUG_BROADCAST_LIGHT) {
                     int seq = r.intent.getIntExtra("seq", -1);
-                    Log.i(TAG, "Sending broadcast " + r.intent.getAction() + " seq=" + seq
-                            + " app=" + filter.receiverList.app);
+                    Log.i(TAG, "Delivering to " + filter.receiverList.app
+                            + " (seq=" + seq + "): " + r);
                 }
                 performReceive(filter.receiverList.app, filter.receiverList.receiver,
                     new Intent(r.intent), r.resultCode,
@@ -10662,6 +11343,9 @@
                     r.receiver = null;
                     r.curFilter = null;
                     filter.receiverList.curBroadcast = null;
+                    if (filter.receiverList.app != null) {
+                        filter.receiverList.app.curReceiver = null;
+                    }
                 }
             }
         }
@@ -10685,6 +11369,8 @@
             while (mParallelBroadcasts.size() > 0) {
                 r = mParallelBroadcasts.remove(0);
                 final int N = r.receivers.size();
+                if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing parallel broadcast "
+                        + r);
                 for (int i=0; i<N; i++) {
                     Object target = r.receivers.get(i);
                     if (DEBUG_BROADCAST)  Log.v(TAG,
@@ -10692,6 +11378,8 @@
                             + target + ": " + r);
                     deliverToRegisteredReceiver(r, (BroadcastFilter)target, false);
                 }
+                if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Done with parallel broadcast "
+                        + r);
             }
 
             // Now take care of the next serialized one...
@@ -10717,10 +11405,18 @@
                 }
             }
 
+            boolean looped = false;
+            
             do {
                 if (mOrderedBroadcasts.size() == 0) {
                     // No more broadcasts pending, so all done!
                     scheduleAppGcsLocked();
+                    if (looped) {
+                        // If we had finished the last ordered broadcast, then
+                        // make sure all processes have correct oom and sched
+                        // adjustments.
+                        updateOomAdjLocked();
+                    }
                     return;
                 }
                 r = mOrderedBroadcasts.get(0);
@@ -10777,9 +11473,13 @@
                     if (DEBUG_BROADCAST) Log.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
                     mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
 
+                    if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Finished with ordered broadcast "
+                            + r);
+                    
                     // ... and on to the next...
                     mOrderedBroadcasts.remove(0);
                     r = null;
+                    looped = true;
                     continue;
                 }
             } while (r == null);
@@ -10793,6 +11493,8 @@
             if (recIdx == 0) {
                 r.dispatchTime = r.startTime;
 
+                if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing ordered broadcast "
+                        + r);
                 if (DEBUG_BROADCAST) Log.v(TAG,
                         "Submitting BROADCAST_TIMEOUT_MSG for "
                         + (r.startTime + BROADCAST_TIMEOUT));
@@ -11135,6 +11837,11 @@
                 Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
                         null, false, false, MY_PID, Process.SYSTEM_UID);
+                
+                AttributeCache ac = AttributeCache.instance();
+                if (ac != null) {
+                    ac.updateConfiguration(mConfiguration);
+                }
             }
         }
         
@@ -11380,6 +12087,14 @@
         app.curRawAdj = adj;
         app.curAdj = adj <= app.maxAdj ? adj : app.maxAdj;
 
+        if (mBackupTarget != null && app == mBackupTarget.app) {
+            // If possible we want to avoid killing apps while they're being backed up
+            if (adj > BACKUP_APP_ADJ) {
+                if (DEBUG_BACKUP) Log.v(TAG, "oom BACKUP_APP_ADJ for " + app);
+                adj = BACKUP_APP_ADJ;
+            }
+        }
+
         if (app.services.size() != 0 && adj > FOREGROUND_APP_ADJ) {
             // If this process has active services running in it, we would
             // like to avoid killing it unless it would prevent the current
@@ -11506,7 +12221,10 @@
         }
 
         app.curAdj = adj;
-
+        app.curSchedGroup = (adj > VISIBLE_APP_ADJ && !app.persistent)
+                ? Process.THREAD_GROUP_BG_NONINTERACTIVE
+                : Process.THREAD_GROUP_DEFAULT;
+        
         return adj;
     }
 
@@ -11651,6 +12369,32 @@
                     return false;
                 }
             }
+            if (app.setSchedGroup != app.curSchedGroup) {
+                app.setSchedGroup = app.curSchedGroup;
+                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Log.v(TAG,
+                        "Setting process group of " + app.processName
+                        + " to " + app.curSchedGroup);
+                if (true) {
+                    long oldId = Binder.clearCallingIdentity();
+                    try {
+                        Process.setProcessGroup(app.pid, app.curSchedGroup);
+                    } catch (Exception e) {
+                        Log.w(TAG, "Failed setting process group of " + app.pid
+                                + " to " + app.curSchedGroup);
+                        e.printStackTrace();
+                    } finally {
+                        Binder.restoreCallingIdentity(oldId);
+                    }
+                }
+                if (false) {
+                    if (app.thread != null) {
+                        try {
+                            app.thread.setSchedulingGroup(app.curSchedGroup);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                }
+            }
         }
 
         return true;
@@ -11942,51 +12686,63 @@
     }
 
     public boolean profileControl(String process, boolean start,
-            String path) throws RemoteException {
+            String path, ParcelFileDescriptor fd) throws RemoteException {
 
-        synchronized (this) {
-            // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
-            // its own permission.
-            if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
-                    != PackageManager.PERMISSION_GRANTED) {
-                throw new SecurityException("Requires permission "
-                        + android.Manifest.permission.SET_ACTIVITY_WATCHER);
-            }
-            
-            ProcessRecord proc = null;
-            try {
-                int pid = Integer.parseInt(process);
-                synchronized (mPidsSelfLocked) {
-                    proc = mPidsSelfLocked.get(pid);
+        try {
+            synchronized (this) {
+                // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+                // its own permission.
+                if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Requires permission "
+                            + android.Manifest.permission.SET_ACTIVITY_WATCHER);
                 }
-            } catch (NumberFormatException e) {
-            }
-            
-            if (proc == null) {
-                HashMap<String, SparseArray<ProcessRecord>> all
-                        = mProcessNames.getMap();
-                SparseArray<ProcessRecord> procs = all.get(process);
-                if (procs != null && procs.size() > 0) {
-                    proc = procs.valueAt(0);
+                
+                if (start && fd == null) {
+                    throw new IllegalArgumentException("null fd");
                 }
-            }
-            
-            if (proc == null || proc.thread == null) {
-                throw new IllegalArgumentException("Unknown process: " + process);
-            }
-            
-            boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
-            if (isSecure) {
-                if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
-                    throw new SecurityException("Process not debuggable: " + proc);
+                
+                ProcessRecord proc = null;
+                try {
+                    int pid = Integer.parseInt(process);
+                    synchronized (mPidsSelfLocked) {
+                        proc = mPidsSelfLocked.get(pid);
+                    }
+                } catch (NumberFormatException e) {
                 }
-            }
+                
+                if (proc == null) {
+                    HashMap<String, SparseArray<ProcessRecord>> all
+                            = mProcessNames.getMap();
+                    SparseArray<ProcessRecord> procs = all.get(process);
+                    if (procs != null && procs.size() > 0) {
+                        proc = procs.valueAt(0);
+                    }
+                }
+                
+                if (proc == null || proc.thread == null) {
+                    throw new IllegalArgumentException("Unknown process: " + process);
+                }
+                
+                boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
+                if (isSecure) {
+                    if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+                        throw new SecurityException("Process not debuggable: " + proc);
+                    }
+                }
             
-            try {
-                proc.thread.profilerControl(start, path);
+                proc.thread.profilerControl(start, path, fd);
+                fd = null;
                 return true;
-            } catch (RemoteException e) {
-                throw new IllegalStateException("Process disappeared");
+            }
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Process disappeared");
+        } finally {
+            if (fd != null) {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index 3fcfad0..33894d6 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -19,17 +19,22 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Log;
 
 class AppErrorDialog extends BaseErrorDialog {
+    private final static String TAG = "AppErrorDialog";
+
     private final AppErrorResult mResult;
     private final ProcessRecord mProc;
 
     // Event 'what' codes
     static final int FORCE_QUIT = 0;
     static final int DEBUG = 1;
+    static final int FORCE_QUIT_AND_REPORT = 2;
 
     // 5-minute timeout, then we automatically dismiss the crash dialog
     static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -58,12 +63,22 @@
 
         setCancelable(false);
 
-        setButton(res.getText(com.android.internal.R.string.force_close),
-                    mHandler.obtainMessage(FORCE_QUIT));
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                res.getText(com.android.internal.R.string.force_close),
+                mHandler.obtainMessage(FORCE_QUIT));
+
         if ((flags&1) != 0) {
-            setButton(res.getText(com.android.internal.R.string.debug),
+            setButton(DialogInterface.BUTTON_NEUTRAL,
+                    res.getText(com.android.internal.R.string.debug),
                     mHandler.obtainMessage(DEBUG));
         }
+
+        if (app.errorReportReceiver != null) {
+            setButton(DialogInterface.BUTTON_NEGATIVE,
+                    res.getText(com.android.internal.R.string.report),
+                    mHandler.obtainMessage(FORCE_QUIT_AND_REPORT));
+        }
+
         setTitle(res.getText(com.android.internal.R.string.aerr_title));
         getWindow().addFlags(FLAG_SYSTEM_ERROR);
         getWindow().setTitle("Application Error: " + app.info.processName);
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index 7390ed0..03c2a04 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -18,7 +18,10 @@
 
 import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Message;
@@ -26,6 +29,13 @@
 import android.util.Log;
 
 class AppNotRespondingDialog extends BaseErrorDialog {
+    private static final String TAG = "AppNotRespondingDialog";
+
+    // Event 'what' codes
+    static final int FORCE_CLOSE = 1;
+    static final int WAIT = 2;
+    static final int WAIT_AND_REPORT = 3;
+
     private final ActivityManagerService mService;
     private final ProcessRecord mProc;
     
@@ -67,10 +77,19 @@
                 ? res.getString(resid, name1.toString(), name2.toString())
                 : res.getString(resid, name1.toString()));
 
-        setButton(res.getText(com.android.internal.R.string.force_close),
-                mHandler.obtainMessage(1));
-        setButton2(res.getText(com.android.internal.R.string.wait),
-                mHandler.obtainMessage(2));
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                res.getText(com.android.internal.R.string.force_close),
+                mHandler.obtainMessage(FORCE_CLOSE));
+        setButton(DialogInterface.BUTTON_NEUTRAL,
+                res.getText(com.android.internal.R.string.wait),
+                mHandler.obtainMessage(WAIT));
+
+        if (app.errorReportReceiver != null) {
+            setButton(DialogInterface.BUTTON_NEGATIVE,
+                    res.getText(com.android.internal.R.string.report),
+                    mHandler.obtainMessage(WAIT_AND_REPORT));
+        }
+
         setTitle(res.getText(com.android.internal.R.string.anr_title));
         getWindow().addFlags(FLAG_SYSTEM_ERROR);
         getWindow().setTitle("Application Not Responding: " + app.info.processName);
@@ -81,16 +100,23 @@
 
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
+            Intent appErrorIntent = null;
             switch (msg.what) {
-                case 1:
+                case FORCE_CLOSE:
                     // Kill the application.
                     mService.killAppAtUsersRequest(mProc,
                             AppNotRespondingDialog.this, true);
                     break;
-                case 2:
+                case WAIT_AND_REPORT:
+                case WAIT:
                     // Continue waiting for the application.
                     synchronized (mService) {
                         ProcessRecord app = mProc;
+
+                        if (msg.what == WAIT_AND_REPORT) {
+                            appErrorIntent = mService.createAppErrorIntentLocked(app);
+                        }
+
                         app.notResponding = false;
                         app.notRespondingReport = null;
                         if (app.anrDialog == AppNotRespondingDialog.this) {
@@ -99,6 +125,14 @@
                     }
                     break;
             }
+
+            if (appErrorIntent != null) {
+                try {
+                    getContext().startActivity(appErrorIntent);
+                } catch (ActivityNotFoundException e) {
+                    Log.w(TAG, "bug report receiver dissappeared", e);
+                }
+            }
         }
     };
 }
diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java
new file mode 100644
index 0000000..5ac8e0d
--- /dev/null
+++ b/services/java/com/android/server/am/BackupRecord.java
@@ -0,0 +1,57 @@
+/*
+ * 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.am;
+
+import com.android.internal.os.BatteryStatsImpl;
+
+import android.content.pm.ApplicationInfo;
+
+/** @hide */
+class BackupRecord {
+    // backup/restore modes
+    public static final int BACKUP_NORMAL = 0;
+    public static final int BACKUP_FULL = 1;
+    public static final int RESTORE = 2;
+    
+    final BatteryStatsImpl.Uid.Pkg.Serv stats;
+    String stringName;                     // cached toString() output
+    final ApplicationInfo appInfo;         // information about BackupAgent's app
+    final int backupMode;                  // full backup / incremental / restore
+    ProcessRecord app;                     // where this agent is running or null
+
+    // ----- Implementation -----
+
+    BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo,
+            int _backupMode) {
+        stats = _agentStats;
+        appInfo = _appInfo;
+        backupMode = _backupMode;
+    }
+
+    public String toString() {
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("BackupRecord{")
+            .append(Integer.toHexString(System.identityHashCode(this)))
+            .append(' ').append(appInfo.packageName)
+            .append(' ').append(appInfo.name)
+            .append(' ').append(appInfo.backupAgentName).append('}');
+        return stringName = sb.toString();
+    }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 0387be5..39a1ee0 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -16,17 +16,19 @@
 
 package com.android.server.am;
 
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.os.BatteryStatsImpl;
-
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Process;
 import android.os.ServiceManager;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BatteryStatsImpl;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
@@ -177,10 +179,10 @@
         }
     }
     
-    public void notePhoneSignalStrength(int asu) {
+    public void notePhoneSignalStrength(SignalStrength signalStrength) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.notePhoneSignalStrengthLocked(asu);
+            mStats.notePhoneSignalStrengthLocked(signalStrength);
         }
     }
     
@@ -190,7 +192,14 @@
             mStats.notePhoneDataConnectionStateLocked(dataType, hasData);
         }
     }
-    
+
+    public void noteAirplaneMode(boolean airplaneMode) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAirplaneModeLocked(airplaneMode);
+        }
+    }
+
     public void noteWifiOn(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -205,6 +214,34 @@
         }
     }
 
+    public void noteStartAudio(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAudioOnLocked(uid);
+        }
+    }
+
+    public void noteStopAudio(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteAudioOffLocked(uid);
+        }
+    }
+
+    public void noteStartVideo(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteVideoOnLocked(uid);
+        }
+    }
+
+    public void noteStopVideo(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteVideoOffLocked(uid);
+        }
+    }
+
     public void noteWifiRunning() {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 4057ae8..da55049 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -16,7 +16,7 @@
 
 package com.android.server.am;
 
-import android.app.IIntentReceiver;
+import android.content.IIntentReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 1488791..b3fc313 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -66,6 +66,7 @@
     int theme;              // resource identifier of activity's theme.
     TaskRecord task;        // the task this is in.
     long startTime;         // when we starting launching this activity
+    long cpuTimeAtResume;   // the cpu time of host process at the time of resuming activity
     Configuration configuration; // configuration activity was last running in
     HistoryRecord resultTo; // who started this entry, so will get our reply
     final String resultWho; // additional identifier for use by resultTo.
@@ -85,6 +86,7 @@
     boolean launchFailed;   // set if a launched failed, to abort on 2nd try
     boolean haveState;      // have we gotten the last activity state?
     boolean stopped;        // is activity pause finished?
+    boolean delayedResume;  // not yet resumed because of stopped app switches?
     boolean finishing;      // activity in pending finish list?
     boolean configDestroy;  // need to destroy due to config change?
     int configChangeFlags;  // which config values have changed
@@ -146,6 +148,7 @@
                 pw.print(" icicle="); pw.println(icicle);
         pw.print(prefix); pw.print("state="); pw.print(state);
                 pw.print(" stopped="); pw.print(stopped);
+                pw.print(" delayedResume="); pw.print(delayedResume);
                 pw.print(" finishing="); pw.println(finishing);
         pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
                 pw.print(" inHistory="); pw.print(inHistory);
@@ -191,6 +194,7 @@
         launchFailed = false;
         haveState = false;
         stopped = false;
+        delayedResume = false;
         finishing = false;
         configDestroy = false;
         keysPaused = false;
@@ -459,6 +463,12 @@
                     return false;
                 }
                 
+                if (service.mDidDexOpt) {
+                    // Give more time since we were dexopting.
+                    service.mDidDexOpt = false;
+                    return false;
+                }
+                
                 if (r.app.instrumentationClass == null) { 
                     service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut");
                 } else {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 4381392..fa2a100 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -17,8 +17,8 @@
 package com.android.server.am;
 
 import android.app.IActivityManager;
-import android.app.IIntentSender;
-import android.app.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.IIntentReceiver;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.os.Binder;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 68aebc3..3f59710 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -56,6 +56,8 @@
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
     int curAdj;                 // Current OOM adjustment for this process
     int setAdj;                 // Last set OOM adjustment for this process
+    int curSchedGroup;          // Currently desired scheduling class
+    int setSchedGroup;          // Last set to background scheduling class
     boolean isForeground;       // Is this app running the foreground UI?
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean foregroundServices; // Running any services that are foreground?
@@ -107,6 +109,10 @@
     ActivityManager.ProcessErrorStateInfo crashingReport;
     ActivityManager.ProcessErrorStateInfo notRespondingReport;
 
+    // Who will be notified of the error. This is usually an activity in the
+    // app that installed the package.
+    ComponentName errorReportReceiver;
+
     void dump(PrintWriter pw, String prefix) {
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
@@ -143,6 +149,8 @@
                 pw.print(" setRaw="); pw.print(setRawAdj);
                 pw.print(" cur="); pw.print(curAdj);
                 pw.print(" set="); pw.println(setAdj);
+        pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
+                pw.print(" setSchedGroup="); pw.println(setSchedGroup);
         pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
                 pw.print(" setIsForeground="); pw.print(setIsForeground);
                 pw.print(" foregroundServices="); pw.print(foregroundServices);
@@ -157,7 +165,14 @@
                     pw.print(" "); pw.print(crashDialog);
                     pw.print(" notResponding="); pw.print(notResponding);
                     pw.print(" " ); pw.print(anrDialog);
-                    pw.print(" bad="); pw.println(bad);
+                    pw.print(" bad="); pw.print(bad);
+
+                    // crashing or notResponding is always set before errorReportReceiver
+                    if (errorReportReceiver != null) {
+                        pw.print(" errorReportReceiver=");
+                        pw.print(errorReportReceiver.flattenToShortString());
+                    }
+                    pw.println();
         }
         if (activities.size() > 0) {
             pw.print(prefix); pw.print("activities="); pw.println(activities);
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 0facefc..32c24c6 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -16,7 +16,7 @@
 
 package com.android.server.am;
 
-import android.app.IIntentReceiver;
+import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Bundle;
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 866334b..2d58659 100755
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -56,9 +56,9 @@
     private static final String TAG = "UsageStats";
     
     // Current on-disk Parcel version
-    private static final int VERSION = 1004;
+    private static final int VERSION = 1005;
 
-    private static final int CHECKIN_VERSION = 3;
+    private static final int CHECKIN_VERSION = 4;
     
     private static final String FILE_PREFIX = "usage-";
     
@@ -82,7 +82,9 @@
     // this lock held.
     final Object mFileLock;
     // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
-    private String mResumedPkg;
+    private String mLastResumedPkg;
+    private String mLastResumedComp;
+    private boolean mIsResumed;
     private File mFile;
     private String mFileLeaf;
     //private File mBackupFile;
@@ -92,11 +94,16 @@
     private int mLastWriteDay;
     
     static class TimeStats {
+        int count;
         int[] times = new int[NUM_LAUNCH_TIME_BINS];
         
         TimeStats() {
         }
         
+        void incCount() {
+            count++;
+        }
+        
         void add(int val) {
             final int[] bins = LAUNCH_TIME_BINS;
             for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
@@ -109,6 +116,7 @@
         }
         
         TimeStats(Parcel in) {
+            count = in.readInt();
             final int[] localTimes = times;
             for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
                 localTimes[i] = in.readInt();
@@ -116,6 +124,7 @@
         }
         
         void writeToParcel(Parcel out) {
+            out.writeInt(count);
             final int[] localTimes = times;
             for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
                 out.writeInt(localTimes[i]);
@@ -152,8 +161,10 @@
             }
         }
         
-        void updateResume() {
-            mLaunchCount ++;
+        void updateResume(boolean launched) {
+            if (launched) {
+                mLaunchCount ++;
+            }
             mResumedTime = SystemClock.elapsedRealtime();
         }
         
@@ -162,6 +173,15 @@
             mUsageTime += (mPausedTime - mResumedTime);
         }
         
+        void addLaunchCount(String comp) {
+            TimeStats times = mLaunchTimes.get(comp);
+            if (times == null) {
+                times = new TimeStats();
+                mLaunchTimes.put(comp, times);
+            }
+            times.incCount();
+        }
+        
         void addLaunchTime(String comp, int millis) {
             TimeStats times = mLaunchTimes.get(comp);
             if (times == null) {
@@ -436,43 +456,70 @@
     public void noteResumeComponent(ComponentName componentName) {
         enforceCallingPermission();
         String pkgName;
-        if ((componentName == null) ||
-                ((pkgName = componentName.getPackageName()) == null)) {
-            return;
-        }
-        if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) {
-            // Moving across activities in same package. just return
-            return;
-        } 
-        if (localLOGV) Log.i(TAG, "started component:"+pkgName);
         synchronized (mStatsLock) {
+            if ((componentName == null) ||
+                    ((pkgName = componentName.getPackageName()) == null)) {
+                return;
+            }
+            
+            final boolean samePackage = pkgName.equals(mLastResumedPkg);
+            if (mIsResumed) {
+                if (samePackage) {
+                    Log.w(TAG, "Something wrong here, didn't expect "
+                            + pkgName + " to be resumed");
+                    return;
+                }
+                
+                if (mLastResumedPkg != null) {
+                    // We last resumed some other package...  just pause it now
+                    // to recover.
+                    Log.w(TAG, "Unexpected resume of " + pkgName
+                            + " while already resumed in " + mLastResumedPkg);
+                    PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
+                    if (pus != null) {
+                        pus.updatePause();
+                    }
+                }
+            }
+            
+            final boolean sameComp = samePackage
+                    && componentName.getClassName().equals(mLastResumedComp);
+            
+            mIsResumed = true;
+            mLastResumedPkg = pkgName;
+            mLastResumedComp = componentName.getClassName();
+            
+            if (localLOGV) Log.i(TAG, "started component:" + pkgName);
             PkgUsageStatsExtended pus = mStats.get(pkgName);
             if (pus == null) {
                 pus = new PkgUsageStatsExtended();
                 mStats.put(pkgName, pus);
             }
-            pus.updateResume();
+            pus.updateResume(!samePackage);
+            if (!sameComp) {
+                pus.addLaunchCount(mLastResumedComp);
+            }
         }
-        mResumedPkg = pkgName;
     }
 
     public void notePauseComponent(ComponentName componentName) {
         enforceCallingPermission();
-        String pkgName;
-        if ((componentName == null) ||
-                ((pkgName = componentName.getPackageName()) == null)) {
-            return;
-        }
-        if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) {
-            Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused");
-            return;
-        }
-        if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
-        
-        // Persist current data to file if needed.
-        writeStatsToFile(false);
         
         synchronized (mStatsLock) {
+            String pkgName;
+            if ((componentName == null) ||
+                    ((pkgName = componentName.getPackageName()) == null)) {
+                return;
+            }
+            if (!mIsResumed) {
+                Log.w(TAG, "Something wrong here, didn't expect "
+                        + pkgName + " to be paused");
+                return;
+            }
+            mIsResumed = false;
+            
+            if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
+        
             PkgUsageStatsExtended pus = mStats.get(pkgName);
             if (pus == null) {
                 // Weird some error here
@@ -481,6 +528,9 @@
             }
             pus.updatePause();
         }
+        
+        // Persist current data to file if needed.
+        writeStatsToFile(false);
     }
     
     public void noteLaunchTime(ComponentName componentName, int millis) {
@@ -631,9 +681,9 @@
             if (isCompactOutput) {
                 sb.append("P:");
                 sb.append(pkgName);
-                sb.append(",");
+                sb.append(',');
                 sb.append(pus.mLaunchCount);
-                sb.append(",");
+                sb.append(',');
                 sb.append(pus.mUsageTime);
                 sb.append('\n');
                 final int NC = pus.mLaunchTimes.size();
@@ -642,6 +692,8 @@
                         sb.append("A:");
                         sb.append(ent.getKey());
                         TimeStats times = ent.getValue();
+                        sb.append(',');
+                        sb.append(times.count);
                         for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
                             sb.append(",");
                             sb.append(times.times[i]);
@@ -665,25 +717,26 @@
                         sb.append("    ");
                         sb.append(ent.getKey());
                         TimeStats times = ent.getValue();
+                        sb.append(": ");
+                        sb.append(times.count);
+                        sb.append(" starts");
                         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(", ");
                                 sb.append(lastBin);
                                 sb.append('-');
                                 sb.append(LAUNCH_TIME_BINS[i]);
-                                sb.append('=');
+                                sb.append("ms=");
                                 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(">=");
                             sb.append(lastBin);
-                            sb.append('=');
+                            sb.append("ms=");
                             sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
                         }
                         sb.append('\n');
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 0b161d6..7a8d4e5e 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -41,6 +41,7 @@
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.text.format.DateFormat;
 import android.util.Log;
@@ -57,6 +58,7 @@
 import com.android.internal.location.GpsLocationProvider;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
 import com.android.internal.telephony.cdma.TtyIntent;
 import com.android.server.am.BatteryStatsService;
 
@@ -97,7 +99,7 @@
     private IBinder mBatteryIcon;
     private IconData mBatteryData;
     private boolean mBatteryFirst = true;
-    private boolean mBatteryPlugged;
+    private int mBatteryPlugged;
     private int mBatteryLevel;
     private int mBatteryThreshold = 0; // index into mBatteryThresholds
     private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
@@ -106,50 +108,146 @@
     private View mBatteryView;
     private int mBatteryViewSequence;
     private boolean mBatteryShowLowOnEndCall = false;
+    private boolean mSentLowBatteryBroadcast = false;
     private static final boolean SHOW_LOW_BATTERY_WARNING = true;
 
     // phone
     private TelephonyManager mPhone;
     private IBinder mPhoneIcon;
+    private IBinder mPhoneEvdoIcon;
 
     //***** Signal strength icons
     private IconData mPhoneData;
+    private IconData mPhoneEvdoData;
     //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,
-            com.android.internal.R.drawable.stat_sys_signal_2,
-            com.android.internal.R.drawable.stat_sys_signal_3,
-            com.android.internal.R.drawable.stat_sys_signal_4
-        };
+        com.android.internal.R.drawable.stat_sys_signal_0,
+        com.android.internal.R.drawable.stat_sys_signal_1,
+        com.android.internal.R.drawable.stat_sys_signal_2,
+        com.android.internal.R.drawable.stat_sys_signal_3,
+        com.android.internal.R.drawable.stat_sys_signal_4
+    };
     private static final int[] sSignalImages_r = new int[] {
-            com.android.internal.R.drawable.stat_sys_r_signal_0,
-            com.android.internal.R.drawable.stat_sys_r_signal_1,
-            com.android.internal.R.drawable.stat_sys_r_signal_2,
-            com.android.internal.R.drawable.stat_sys_r_signal_3,
-            com.android.internal.R.drawable.stat_sys_r_signal_4
-        };
+        com.android.internal.R.drawable.stat_sys_r_signal_0,
+        com.android.internal.R.drawable.stat_sys_r_signal_1,
+        com.android.internal.R.drawable.stat_sys_r_signal_2,
+        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
+        com.android.internal.R.drawable.stat_sys_signal_cdma_0,
+        com.android.internal.R.drawable.stat_sys_signal_cdma_1,
+        com.android.internal.R.drawable.stat_sys_signal_cdma_2,
+        com.android.internal.R.drawable.stat_sys_signal_cdma_3,
+        com.android.internal.R.drawable.stat_sys_signal_cdma_4
     };
-    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[] sRoamingIndicatorImages_cdma = new int[] {
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
+        // 1 is Standard Roaming Indicator OFF
+        // TODO T: image never used, remove and put 0 instead?
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 2 is Standard Roaming Indicator FLASHING
+        // TODO T: image never used, remove and put 0 instead?
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 3-12 Standard ERI
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 13-63 Reserved for Standard ERI
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+        // 64-127 Reserved for Non Standard (Operator Specific) ERI
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+        com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83
+
+        // 128-255 Reserved
     };
-    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
+    // EVDO
+    private static final int[] sSignalImages_evdo = new int[] {
+        com.android.internal.R.drawable.stat_sys_signal_evdo_0,
+        com.android.internal.R.drawable.stat_sys_signal_evdo_1,
+        com.android.internal.R.drawable.stat_sys_signal_evdo_2,
+        com.android.internal.R.drawable.stat_sys_signal_evdo_3,
+        com.android.internal.R.drawable.stat_sys_signal_evdo_4
     };
 
     //***** Data connection icons
@@ -179,12 +277,14 @@
         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,
+        com.android.internal.R.drawable.stat_sys_data_dormant_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,
+        com.android.internal.R.drawable.stat_sys_data_dormant_1xrtt,
     };
 
     // Assume it's all good unless we hear otherwise.  We don't always seem
@@ -194,7 +294,7 @@
     int mDataState = TelephonyManager.DATA_DISCONNECTED;
     int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
     ServiceState mServiceState;
-    int mSignalAsu = -1;
+    SignalStrength mSignalStrength;
 
     // data connection
     private IBinder mDataIcon;
@@ -249,6 +349,10 @@
     private IBinder mTTYModeIcon;
     private IconData mTTYModeEnableIconData;
 
+    // Cdma Roaming Indicator, ERI
+    private IBinder mCdmaRoamingIndicatorIcon;
+    private IconData mCdmaRoamingIndicatorIconData;
+
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -306,6 +410,7 @@
     private StatusBarPolicy(Context context, StatusBarService service) {
         mContext = context;
         mService = service;
+        mSignalStrength = new SignalStrength();
         mBatteryStats = BatteryStatsService.getService();
 
         // clock
@@ -321,14 +426,21 @@
 
         // phone_signal
         mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
-        mPhoneData = IconData.makeIcon("phone_signal", 
+        mPhoneData = IconData.makeIcon("phone_signal",
                 null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
         mPhoneIcon = service.addIcon(mPhoneData, null);
+
+        // phone_evdo_signal
+        mPhoneEvdoData = IconData.makeIcon("phone_evdo_signal",
+                null, com.android.internal.R.drawable.stat_sys_signal_evdo_0, 0, 0);
+        mPhoneEvdoIcon = service.addIcon(mPhoneEvdoData, null);
+        service.setIconVisibility(mPhoneEvdoIcon, false);
+
         // register for phone state notifications.
         ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
                 .listen(mPhoneStateListener,
                           PhoneStateListener.LISTEN_SERVICE_STATE
-                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTH
+                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
                         | PhoneStateListener.LISTEN_CALL_STATE
                         | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
                         | PhoneStateListener.LISTEN_DATA_ACTIVITY);
@@ -351,6 +463,12 @@
         mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
         service.setIconVisibility(mTTYModeIcon, false);
 
+        // Cdma Roaming Indicator, ERI
+        mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri",
+                null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0);
+        mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null);
+        service.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+
         // bluetooth status
         mBluetoothData = IconData.makeIcon("bluetooth",
                 null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
@@ -464,7 +582,7 @@
         mBatteryData.iconLevel = intent.getIntExtra("level", 0);
         mService.updateIcon(mBatteryIcon, mBatteryData, null);
 
-        boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+        int plugged = intent.getIntExtra("plugged", 0);
         int level = intent.getIntExtra("level", -1);
         if (false) {
             Log.d(TAG, "updateBattery level=" + level
@@ -475,7 +593,7 @@
                     + " mBatteryFirst=" + mBatteryFirst);
         }
 
-        boolean oldPlugged = mBatteryPlugged;
+        int oldPlugged = mBatteryPlugged;
         int oldThreshold = mBatteryThreshold;
         pickNextBatteryLevel(level);
 
@@ -502,11 +620,12 @@
             Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
                     + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
         }
-        if (!plugged
-                && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
+        if (plugged == 0
+                && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
                     || (mBatteryThreshold > oldThreshold
                         && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
             // Broadcast the low battery warning
+            mSentLowBatteryBroadcast = true;
             mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
 
             if (SHOW_LOW_BATTERY_WARNING) {
@@ -522,7 +641,11 @@
                     mBatteryShowLowOnEndCall = true;
                 }
             }
-        } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) {
+        } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) {
+            if (mSentLowBatteryBroadcast == true) {
+                mSentLowBatteryBroadcast = false;
+                mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
+            }
             if (SHOW_LOW_BATTERY_WARNING) {
                 if (mLowBatteryDialog != null) {
                     mLowBatteryDialog.dismiss();
@@ -611,6 +734,23 @@
                 b.setView(v);
                 b.setIcon(android.R.drawable.ic_dialog_alert);
                 b.setPositiveButton(android.R.string.ok, null);
+                
+                final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                        | Intent.FLAG_ACTIVITY_NO_HISTORY);
+                if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+                    b.setNegativeButton(com.android.internal.R.string.battery_low_why,
+                            new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            mContext.startActivity(intent);
+                            if (mLowBatteryDialog != null) {
+                                mLowBatteryDialog.dismiss();
+                            }
+                        }
+                    });
+                }
 
             AlertDialog d = b.create();
             d.setOnDismissListener(mLowBatteryListener);
@@ -629,7 +769,7 @@
         }
         if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
             if (mBatteryShowLowOnEndCall) {
-                if (!mBatteryPlugged) {
+                if (mBatteryPlugged == 0) {
                     showLowBatteryWarning();
                 }
                 mBatteryShowLowOnEndCall = false;
@@ -666,8 +806,8 @@
 
     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
-        public void onSignalStrengthChanged(int asu) {
-            mSignalAsu = asu;
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            mSignalStrength = signalStrength;
             updateSignalStrength();
         }
 
@@ -675,6 +815,7 @@
         public void onServiceStateChanged(ServiceState state) {
             mServiceState = state;
             updateSignalStrength();
+            updateCdmaRoamingIcon();
             updateDataIcon();
         }
 
@@ -696,7 +837,6 @@
             updateDataIcon();
         }
     };
-    
 
     private final void updateSimState(Intent intent) {
         String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
@@ -723,25 +863,31 @@
         updateDataIcon();
     }
 
-    private final void updateSignalStrength() {
-        int asu = mSignalAsu;
-        ServiceState ss = mServiceState;
+    private boolean isCdma() {
+        return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA));
+    }
 
-        boolean hasService = true;
-        
-        if (ss != null) {
-            int state = ss.getState();
-            switch (state) {
+    private boolean hasService() {
+        if (mServiceState != null) {
+            switch (mServiceState.getState()) {
                 case ServiceState.STATE_OUT_OF_SERVICE:
                 case ServiceState.STATE_POWER_OFF:
-                    hasService = false;
-                    break;
+                    return false;
+                default:
+                    return true;
             }
         } else {
-            hasService = false;
+            return false;
         }
+    }
 
-        if (!hasService) {
+    private final void updateSignalStrength() {
+        int iconLevel = -1;
+        int evdoIconLevel = -1;
+        int[] iconList;
+        int[] evdoIconList;
+
+        if (!hasService()) {
             //Log.d(TAG, "updateSignalStrength: no service");
             if (Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
@@ -750,48 +896,92 @@
                 mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
             }
             mService.updateIcon(mPhoneIcon, mPhoneData, null);
+            mService.setIconVisibility(mPhoneEvdoIcon,false);
             return;
         }
 
-        // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-        // asu = 0 (-113dB or less) is very weak
-        // signal, its better to show 0 bars to the user in such cases.
-        // asu = 99 is a special case, where the signal strength is unknown.
-        if (asu <= 0 || asu == 99) asu = 0;
-        else if (asu >= 16) asu = 4;
-        else if (asu >= 8)  asu = 3;
-        else if (asu >= 4)  asu = 2;
-        else asu = 1;
+        if (!isCdma()) {
+            int asu = mSignalStrength.getGsmSignalStrength();
 
-        int[] iconList;
-        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;
+            // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+            // asu = 0 (-113dB or less) is very weak
+            // signal, its better to show 0 bars to the user in such cases.
+            // asu = 99 is a special case, where the signal strength is unknown.
+            if (asu <= 0 || asu == 99) iconLevel = 0;
+            else if (asu >= 16) iconLevel = 4;
+            else if (asu >= 8)  iconLevel = 3;
+            else if (asu >= 4)  iconLevel = 2;
+            else iconLevel = 1;
+
+            if (mPhone.isNetworkRoaming()) {
+                iconList = sSignalImages_r;
+            } else {
+                iconList = sSignalImages;
             }
-        } else if (mPhone.isNetworkRoaming()) {
-            iconList = sSignalImages_r;
         } else {
-            iconList = sSignalImages;
+            iconList = this.sSignalImages_cdma;
+
+            int cdmaDbm = mSignalStrength.getCdmaDbm();
+            int cdmaEcio = mSignalStrength.getCdmaEcio();
+            int levelDbm = 0;
+            int levelEcio = 0;
+
+            if (cdmaDbm >= -75) levelDbm = 4;
+            else if (cdmaDbm >= -85) levelDbm = 3;
+            else if (cdmaDbm >= -95) levelDbm = 2;
+            else if (cdmaDbm >= -100) levelDbm = 1;
+            else levelDbm = 0;
+
+            // Ec/Io are in dB*10
+            if (cdmaEcio >= -90) levelEcio = 4;
+            else if (cdmaEcio >= -110) levelEcio = 3;
+            else if (cdmaEcio >= -130) levelEcio = 2;
+            else if (cdmaEcio >= -150) levelEcio = 1;
+            else levelEcio = 0;
+
+            iconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
         }
 
-        mPhoneData.iconId = iconList[asu];
+        if ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+                  || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) {
+            // Use Evdo icon
+            evdoIconList = this.sSignalImages_evdo;
+
+            int evdoEcio = mSignalStrength.getEvdoEcio();
+            int evdoSnr = mSignalStrength.getEvdoSnr();
+            int levelEvdoEcio = 0;
+            int levelEvdoSnr = 0;
+
+            // Ec/Io are in dB*10
+            if (evdoEcio >= -650) levelEvdoEcio = 4;
+            else if (evdoEcio >= -750) levelEvdoEcio = 3;
+            else if (evdoEcio >= -900) levelEvdoEcio = 2;
+            else if (evdoEcio >= -1050) levelEvdoEcio = 1;
+            else levelEvdoEcio = 0;
+
+            if (evdoSnr > 7) levelEvdoSnr = 4;
+            else if (evdoSnr > 5) levelEvdoSnr = 3;
+            else if (evdoSnr > 3) levelEvdoSnr = 2;
+            else if (evdoSnr > 1) levelEvdoSnr = 1;
+            else levelEvdoSnr = 0;
+
+            evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
+
+            mPhoneEvdoData.iconId = evdoIconList[evdoIconLevel];
+            mService.updateIcon(mPhoneEvdoIcon, mPhoneEvdoData, null);
+            mService.setIconVisibility(mPhoneEvdoIcon,true);
+        } else {
+            mService.setIconVisibility(mPhoneEvdoIcon,false);
+        }
+
+        mPhoneData.iconId = iconList[iconLevel];
         mService.updateIcon(mPhoneIcon, mPhoneData, null);
     }
 
     private final void updateDataNetType() {
         int net = mPhone.getNetworkType();
-        ServiceState ss = this.mServiceState;
 
         switch (net) {
-
         case TelephonyManager.NETWORK_TYPE_EDGE:
             mDataIconList = sDataNetType_e;
             break;
@@ -819,32 +1009,51 @@
         int iconId;
         boolean visible = true;
 
-        if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
-            int data = mDataState;
-
-            int[] list = mDataIconList;
-
-            ServiceState ss = mServiceState;
-
-            boolean hasService = false;
-
-            if (ss != null) {
-                hasService = (ss.getState() == ServiceState.STATE_IN_SERVICE);
+        if (!isCdma()) {
+            // GSM case, we have to check also the sim state
+            if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
+                if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+                    switch (mDataActivity) {
+                        case TelephonyManager.DATA_ACTIVITY_IN:
+                            iconId = mDataIconList[1];
+                            break;
+                        case TelephonyManager.DATA_ACTIVITY_OUT:
+                            iconId = mDataIconList[2];
+                            break;
+                        case TelephonyManager.DATA_ACTIVITY_INOUT:
+                            iconId = mDataIconList[3];
+                            break;
+                        default:
+                            iconId = mDataIconList[0];
+                            break;
+                    }
+                    mDataData.iconId = iconId;
+                    mService.updateIcon(mDataIcon, mDataData, null);
+                } else {
+                    visible = false;
+                }
+            } else {
+                mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
+                mService.updateIcon(mDataIcon, mDataData, null);
             }
-
-            if (hasService && data == TelephonyManager.DATA_CONNECTED) {
+        } else {
+            // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
+            if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
                 switch (mDataActivity) {
                     case TelephonyManager.DATA_ACTIVITY_IN:
-                        iconId = list[1];
+                        iconId = mDataIconList[1];
                         break;
                     case TelephonyManager.DATA_ACTIVITY_OUT:
-                        iconId = list[2];
+                        iconId = mDataIconList[2];
                         break;
                     case TelephonyManager.DATA_ACTIVITY_INOUT:
-                        iconId = list[3];
+                        iconId = mDataIconList[3];
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
+                        iconId = mDataIconList[4];
                         break;
                     default:
-                        iconId = list[0];
+                        iconId = mDataIconList[0];
                         break;
                 }
                 mDataData.iconId = iconId;
@@ -852,10 +1061,8 @@
             } else {
                 visible = false;
             }
-        } else {
-            mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
-            mService.updateIcon(mDataIcon, mDataData, null);
         }
+
         long ident = Binder.clearCallingIdentity();
         try {
             mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
@@ -863,6 +1070,7 @@
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+
         if (mDataIconVisible != visible) {
             mService.setIconVisibility(mDataIcon, visible);
             mDataIconVisible = visible;
@@ -873,7 +1081,7 @@
         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         final int ringerMode = audioManager.getRingerMode();
         final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
-                ringerMode == AudioManager.RINGER_MODE_VIBRATE; 
+                ringerMode == AudioManager.RINGER_MODE_VIBRATE;
         final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
                 ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
                 : com.android.internal.R.drawable.stat_sys_ringer_silent;
@@ -905,7 +1113,7 @@
         } else {
             return;
         }
-        
+
         if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED ||
                 mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED ||
                 mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) {
@@ -920,15 +1128,15 @@
     private final void updateWifi(Intent intent) {
         final String action = intent.getAction();
         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-            
+
             final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                     WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-            
+
             if (!enabled) {
                 // If disabled, hide the icon. (We show icon when connected.)
                 mService.setIconVisibility(mWifiIcon, false);
             }
-            
+
         } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
             final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
                                                            false);
@@ -937,9 +1145,9 @@
             }
         } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
 
-            final NetworkInfo networkInfo = (NetworkInfo) 
+            final NetworkInfo networkInfo = (NetworkInfo)
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-            
+
             int iconId;
             if (networkInfo != null && networkInfo.isConnected()) {
                 mIsWifiConnected = true;
@@ -986,18 +1194,18 @@
         if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
             // GPS is getting fixes
             mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
-            mService.setIconVisibility(mGpsIcon, true);           
+            mService.setIconVisibility(mGpsIcon, true);
         } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
             // GPS is off
-            mService.setIconVisibility(mGpsIcon, false);           
+            mService.setIconVisibility(mGpsIcon, false);
         } else {
             // GPS is on, but not receiving fixes
             mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
-            mService.setIconVisibility(mGpsIcon, true);           
+            mService.setIconVisibility(mGpsIcon, true);
         }
     }
 
-    private final void updateTTY(Intent intent) {       
+    private final void updateTTY(Intent intent) {
         final String action = intent.getAction();
         final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
 
@@ -1007,14 +1215,63 @@
             // TTY is on
             Log.i(TAG, "updateTTY: set TTY on");
             mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
-            mService.setIconVisibility(mTTYModeIcon, true);          
+            mService.setIconVisibility(mTTYModeIcon, true);
         } else {
             // TTY is off
             Log.i(TAG, "updateTTY: set TTY off");
-            mService.setIconVisibility(mTTYModeIcon, false);           
+            mService.setIconVisibility(mTTYModeIcon, false);
         }
     }
 
+    private final void updateCdmaRoamingIcon() {
+        if (!hasService()) {
+            mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
+        }
+
+        if (!isCdma()) {
+            mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
+        }
+
+        int[] iconList = sRoamingIndicatorImages_cdma;
+        int iconIndex = mPhone.getCdmaEriIconIndex();
+        int iconMode = mPhone.getCdmaEriIconMode();
+
+        if (iconIndex == -1) {
+            Log.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
+            return;
+        }
+
+        if (iconMode == -1) {
+            Log.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
+            return;
+        }
+
+        if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
+            Log.d(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
+            mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
+        }
+
+        switch (iconMode) {
+            case EriInfo.ROAMING_ICON_MODE_NORMAL:
+                mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex];
+                mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+                mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
+                break;
+            case EriInfo.ROAMING_ICON_MODE_FLASH:
+                mCdmaRoamingIndicatorIconData.iconId =
+                        com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
+                mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+                mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
+                break;
+
+        }
+        mService.updateIcon(mPhoneIcon, mPhoneData, null);
+    }
+
+
     private class StatusBarHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -1028,6 +1285,3 @@
         }
     }
 }
-
-
-
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 5336e27..b44168a 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -19,6 +19,7 @@
 import com.android.internal.R;
 import com.android.internal.util.CharSequences;
 
+import android.app.ActivityManagerNative;
 import android.app.Dialog;
 import android.app.IStatusBar;
 import android.app.PendingIntent;
@@ -118,6 +119,7 @@
         public void binderDied() {
             Log.i(TAG, "binder died for pkg=" + pkg);
             disable(0, token, pkg);
+            token.unlinkToDeath(this, 0);
         }
     }
 
@@ -493,6 +495,7 @@
             if (what == 0 || !token.isBinderAlive()) {
                 if (tok != null) {
                     mDisableRecords.remove(i);
+                    tok.token.unlinkToDeath(tok, 0);
                 }
             } else {
                 if (tok == null) {
@@ -1254,6 +1257,14 @@
 
         public void onClick(View v) {
             try {
+                // The intent we are sending is for the application, which
+                // won't have permission to immediately start an activity after
+                // the user switches to home.  We know it is safe to do at this
+                // point, so make sure new activity switches are now allowed.
+                ActivityManagerNative.getDefault().resumeAppSwitches();
+            } catch (RemoteException e) {
+            }
+            try {
                 mIntent.send();
                 mNotificationCallbacks.onNotificationClick(mPkg, mId);
             } catch (PendingIntent.CanceledException e) {