Merge "Magnifier not respecting RTL/LTR direction and keyguard dialogs not properly centered." into jb-mr1-dev
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 5cde65c..03e0c0f 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -247,7 +247,6 @@
     }
 
     public void systemReady() {
-        initUser(UserHandle.USER_OWNER);
     }
 
     private UserManager getUserManager() {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7492629..67d3930 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2809,6 +2809,15 @@
         return success;
     }
 
+    public void clearPendingBackup() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(CLEAR_PENDING_BACKUP_TRANSACTION, data, reply, 0);
+        reply.recycle();
+        data.recycle();
+    }
+
     public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5f65f08..456d757 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,6 +29,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
@@ -2396,12 +2397,31 @@
     private void handleCreateBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data);
 
+        // Sanity check the requested target package's uid against ours
+        try {
+            PackageInfo requestedPackage = getPackageManager().getPackageInfo(
+                    data.appInfo.packageName, 0, UserHandle.myUserId());
+            if (requestedPackage.applicationInfo.uid != Process.myUid()) {
+                Slog.w(TAG, "Asked to instantiate non-matching package "
+                        + data.appInfo.packageName);
+                return;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Can't reach package manager", e);
+            return;
+        }
+
         // no longer idle; we have backup work to do
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
         LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
         String packageName = packageInfo.mPackageName;
+        if (packageName == null) {
+            Slog.d(TAG, "Asked to create backup agent for nonexistent package");
+            return;
+        }
+
         if (mBackupAgents.get(packageName) != null) {
             Slog.d(TAG, "BackupAgent " + "  for " + packageName
                     + " already exists");
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 97250e9..8fc1c86 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -152,6 +152,7 @@
     
     public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
             throws RemoteException;
+    public void clearPendingBackup() throws RemoteException;
     public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
     public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
     public void killApplicationProcess(String processName, int uid) throws RemoteException;
@@ -619,4 +620,5 @@
     int GET_RUNNING_USER_IDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+156;
     int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157;
     int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158;
+    int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159;
 }
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 58df167..bb0c686 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -55,6 +55,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.util.IndentingPrintWriter;
@@ -311,13 +312,10 @@
             if (userId == UserHandle.USER_NULL) return;
 
             if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                Log.i(TAG, "User removed: u" + userId);
                 onUserRemoved(userId);
             } else if (Intent.ACTION_USER_STARTING.equals(action)) {
-                Log.i(TAG, "User starting: u" + userId);
                 onUserStarting(userId);
             } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
-                Log.i(TAG, "User stopping: u" + userId);
                 onUserStopping(userId);
             }
         }
@@ -338,6 +336,10 @@
         }
     }
 
+    /**
+     * Should only be created after {@link ContentService#systemReady()} so that
+     * {@link PackageManager} is ready to query.
+     */
     public SyncManager(Context context, boolean factoryTest) {
         // Initialize the SyncStorageEngine first, before registering observers
         // and creating threads and so on; it may fail if the disk is full.
@@ -441,9 +443,6 @@
                     UserHandle.ALL,
                     new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
                     null, null);
-
-            // do this synchronously to ensure we have the accounts before this call returns
-            onUserStarting(UserHandle.USER_OWNER);
         }
 
         // Pick a random second in a day to seed all periodic syncs
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 14bfc5b..c9a325e 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -51,8 +51,6 @@
     public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) {
         mSyncStorageEngine = syncStorageEngine;
         mSyncAdapters = syncAdapters;
-
-        addPendingOperations(UserHandle.USER_OWNER);
     }
 
     public void addPendingOperations(int userId) {
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 4820c5e..6c9290b 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -51,12 +51,17 @@
  * an exhibition/lean-back experience.</p>
  *
  * <p>The Dream lifecycle is as follows:</p>
- * <ul>
- *   <li>onAttachedToWindow</li>
- *   <li>onDreamingStarted</li>
- *   <li>onDreamingStopped</li>
- *   <li>onDetachedFromWindow</li>
- * </ul>
+ * <ol>
+ *   <li>{@link #onAttachedToWindow}
+ *     <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li>
+ *   <li>{@link #onDreamingStarted}
+ *     <p>Your dream has started, so you should begin animations or other behaviors here.</li>
+ *   <li>{@link #onDreamingStopped}
+ *     <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li>
+ *   <li>{@link #onDetachedFromWindow}
+ *     <p>Use this to dismantle resources your dream set up. For example, detach from handlers
+ *        and listeners.</li>
+ * </ol>
  *
  * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but
  * initialization and teardown should be done by overriding the hooks above.</p>
@@ -80,14 +85,40 @@
  *         android:resource="@xml/my_dream" />
  * &lt;/service>
  * </pre>
- * <p>If specified, additional information for the dream is defined using the
- * <code>&lt;{@link android.R.styleable#Dream dream}&gt;</code> element.  For example:</p>
- * <pre>
- * (in res/xml/my_dream.xml)
  *
+ * <p>If specified with the {@code &lt;meta-data&gt;} element,
+ * additional information for the dream is defined using the
+ * {@link android.R.styleable#Dream &lt;dream&gt;} element in a separate XML file.
+ * Currently, the only addtional
+ * information you can provide is for a settings activity that allows the user to configure
+ * the dream behavior. For example:</p>
+ * <p class="code-caption">res/xml/my_dream.xml</p>
+ * <pre>
  * &lt;dream xmlns:android="http://schemas.android.com/apk/res/android"
  *     android:settingsActivity="com.example.app/.MyDreamSettingsActivity" />
  * </pre>
+ * <p>This makes a Settings button available alongside your dream's listing in the
+ * system settings, which when pressed opens the specified activity.</p>
+ *
+ *
+ * <p>To specify your dream layout, call {@link #setContentView}, typically during the
+ * {@link #onAttachedToWindow} callback. For example:</p>
+ * <pre>
+ * public class MyDream extends DreamService {
+ *
+ *     &#64;Override
+ *     public void onAttachedToWindow() {
+ *         super.onAttachedToWindow();
+ *
+ *         // Exit dream upon user touch
+ *         setInteractive(false);
+ *         // Hide system UI
+ *         setFullscreen(true);
+ *         // Set the dream layout
+ *         setContentView(R.layout.dream);
+ *     }
+ * }
+ * </pre>
  */
 public class DreamService extends Service implements Window.Callback {
     private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
@@ -323,11 +354,12 @@
 
     /**
      * Sets a view to be the content view for this Dream.
-     * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)},
+     * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity,
      * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view.
      *
-     * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p>
-     * @param view The desired content to display.
+     * <p>Note: This requires a window, so you should usually call it during
+     * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it
+     * during {@link #onCreate}).</p>
      *
      * @see #setContentView(int)
      * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
@@ -339,9 +371,12 @@
     /**
      * Sets a view to be the content view for this Dream.
      * Behaves similarly to
-     * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}.
+     * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
+     * in an activity.
      *
-     * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p>
+     * <p>Note: This requires a window, so you should usually call it during
+     * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it
+     * during {@link #onCreate}).</p>
      *
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 1b71b43..5d306d2 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -829,7 +829,6 @@
 
 static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz,
         Layer* layer, SkMatrix* matrix) {
-
     layer->getTransform().load(*matrix);
 }
 
diff --git a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png
new file mode 100644
index 0000000..35f27df
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png
new file mode 100644
index 0000000..f9c8678
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png
new file mode 100644
index 0000000..4cc0ee8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c93008d..3e8892b 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1418,7 +1418,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou tablet te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
     <string name="safe_media_volume_warning" product="default" msgid="7382971871993371648">"Moet volume bo veilige vlak verhoog word?"\n"Deur vir lang tydperke op hoë volume te luister, kan jou gehoor beskadig."</string>
-    <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hou twee vingers in om toeganklikheid te aktiveer."</string>
+    <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hou aan met twee vingers inhou om toeganklikheid te aktiveer."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Toeganklikheid geaktiveer."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string>
     <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4698002..afd847f 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -636,19 +636,12 @@
          of new location providers at run-time. The new package does not
          have to be explicitly listed here, however it must have a signature
          that matches the signature of at least one package on this list.
-         Platforms should overlay additional packages in
-         config_overlay_locationProviderPackageNames, instead of overlaying
-         this config, if they only want to append packages and not replace
-         the entire array.
          -->
     <string-array name="config_locationProviderPackageNames" translatable="false">
+        <!-- The standard AOSP fused location provider -->
         <item>com.android.location.fused</item>
     </string-array>
 
-    <!-- Pacakge name(s) supplied by overlay, and appended to
-         config_locationProviderPackageNames. -->
-    <string-array name="config_overlay_locationProviderPackageNames" translatable="false" />
-
     <!-- Boolean indicating if current platform supports bluetooth SCO for off call
     use cases -->
     <bool name="config_bluetooth_sco_off_call">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 72de22c..f8dbd84 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3821,6 +3821,13 @@
     <!-- Title text to show within the overlay.  [CHAR LIMIT=50] -->
     <string name="display_manager_overlay_display_title"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string>
 
+    <!-- Title of the notification to indicate an active wifi display connection.  [CHAR LIMIT=50] -->
+    <string name="wifi_display_notification_title">Wireless display is connected</string>
+    <!-- Message of the notification to indicate an active wifi display connection.  [CHAR LIMIT=80] -->
+    <string name="wifi_display_notification_message">This screen is showing on another device</string>
+    <!-- Label of a button to disconnect an active wifi display connection.  [CHAR LIMIT=25] -->
+    <string name="wifi_display_notification_disconnect">Disconnect</string>
+
     <!-- Keyguard strings -->
     <!-- Label shown on emergency call button in keyguard -->
     <string name="kg_emergency_call_label">Emergency call</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e76b67b..7c0547e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1475,7 +1475,6 @@
   <java-symbol type="array" name="radioAttributes" />
   <java-symbol type="array" name="config_oemUsbModeOverride" />
   <java-symbol type="array" name="config_locationProviderPackageNames" />
-  <java-symbol type="array" name="config_overlay_locationProviderPackageNames" />
   <java-symbol type="bool" name="config_animateScreenLights" />
   <java-symbol type="bool" name="config_automatic_brightness_available" />
   <java-symbol type="bool" name="config_sf_limitedAlpha" />
@@ -1486,6 +1485,7 @@
   <java-symbol type="bool" name="show_ongoing_ime_switcher" />
   <java-symbol type="color" name="config_defaultNotificationColor" />
   <java-symbol type="drawable" name="ic_notification_ime_default" />
+  <java-symbol type="drawable" name="ic_notify_wifidisplay" />
   <java-symbol type="drawable" name="stat_notify_car_mode" />
   <java-symbol type="drawable" name="stat_notify_disabled" />
   <java-symbol type="drawable" name="stat_notify_disk_full" />
@@ -1621,6 +1621,9 @@
   <java-symbol type="string" name="vpn_lockdown_error" />
   <java-symbol type="string" name="vpn_lockdown_reset" />
   <java-symbol type="string" name="wallpaper_binding_label" />
+  <java-symbol type="string" name="wifi_display_notification_title" />
+  <java-symbol type="string" name="wifi_display_notification_message" />
+  <java-symbol type="string" name="wifi_display_notification_disconnect" />
   <java-symbol type="style" name="Theme.Dialog.AppError" />
   <java-symbol type="style" name="Theme.Toast" />
   <java-symbol type="xml" name="storage_list" />
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 62c054a..3cfed13 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -119,7 +119,7 @@
 a {@link android.support.v4.app.DialogFragment}:</p>
 
 <pre>
-public class FireMissilesDialog extends DialogFragment {
+public class FireMissilesDialogFragment extends DialogFragment {
     &#64;Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Use the Builder class for convenient dialog construction
@@ -469,7 +469,7 @@
            })
            .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
-                   NoticeDialog.this.getDialog().cancel();
+                   LoginDialogFragment.this.getDialog().cancel();
                }
            });      
     return builder.create();
@@ -497,15 +497,15 @@
 <p>When the user touches one of the dialog's action buttons or selects an item from its list,
 your {@link android.support.v4.app.DialogFragment} might perform the necessary
 action itself, but often you'll want to deliver the event to the activity or fragment that
-opened the dialog. To do this, define an interface with a method for each type of click event,
-then implement that interface in the host component that will
+opened the dialog. To do this, define an interface with a method for each type of click event.
+Then implement that interface in the host component that will
 receive the action events from the dialog.</p>
 
 <p>For example, here's a {@link android.support.v4.app.DialogFragment} that defines an
 interface through which it delivers the events back to the host activity:</p>
 
 <pre>
-public class NoticeDialog extends DialogFragment {
+public class NoticeDialogFragment extends DialogFragment {
     
     /* The activity that creates an instance of this dialog fragment must
      * implement this interface in order to receive event callbacks.
@@ -516,48 +516,44 @@
     }
     
     // Use this instance of the interface to deliver action events
-    static NoticeDialogListener mListener;
-        
-    /* Call this to instantiate a new NoticeDialog.
-     * @param activity  The activity hosting the dialog, which must implement the
-     *                  NoticeDialogListener to receive event callbacks.
-     * @returns A new instance of NoticeDialog.
-     * @throws  ClassCastException if the host activity does not
-     *          implement NoticeDialogListener
-     */
-    public static NoticeDialog newInstance(Activity activity) {
+    NoticeDialogListener mListener;
+    
+    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
+    &#64;Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
         // Verify that the host activity implements the callback interface
         try {
-            // Instantiate the NoticeDialogListener so we can send events with it
+            // Instantiate the NoticeDialogListener so we can send events to the host
             mListener = (NoticeDialogListener) activity;
         } catch (ClassCastException e) {
             // The activity doesn't implement the interface, throw exception
             throw new ClassCastException(activity.toString()
                     + " must implement NoticeDialogListener");
         }
-        NoticeDialog frag = new NoticeDialog();
-        return frag;
     }
-    
     ...
 }
 </pre>
 
-<p>The activity hosting the dialog creates and shows an instance of the dialog
-by calling {@code NoticeDialog.newInstance()} and receives the dialog's
+<p>The activity hosting the dialog creates an instance of the dialog
+with the dialog fragment's constructor and receives the dialog's
 events through an implementation of the {@code NoticeDialogListener} interface:</p>
 
 <pre>
 public class MainActivity extends FragmentActivity
-                          implements NoticeDialog.NoticeDialogListener{
+                          implements NoticeDialogFragment.NoticeDialogListener{
     ...
     
     public void showNoticeDialog() {
         // Create an instance of the dialog fragment and show it
-        DialogFragment dialog = NoticeDialog.newInstance(this);
-        dialog.show(getSupportFragmentManager(), "NoticeDialog");
+        DialogFragment dialog = new NoticeDialogFragment();
+        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
     }
 
+    // The dialog fragment receives a reference to this Activity through the
+    // Fragment.onAttach() callback, which it uses to call the following methods
+    // defined by the NoticeDialogFragment.NoticeDialogListener interface
     &#64;Override
     public void onDialogPositiveClick(DialogFragment dialog) {
         // User touched the dialog's positive button
@@ -573,11 +569,12 @@
 </pre>
 
 <p>Because the host activity implements the {@code NoticeDialogListener}&mdash;which is
-enforced by the {@code newInstance()} method shown above&mdash;the dialog fragment can use the
+enforced by the {@link android.support.v4.app.Fragment#onAttach onAttach()}
+callback method shown above&mdash;the dialog fragment can use the
 interface callback methods to deliver click events to the activity:</p>
 
 <pre>
-public class NoticeDialog extends DialogFragment {
+public class NoticeDialogFragment extends DialogFragment {
     ...
 
     &#64;Override
@@ -588,13 +585,13 @@
                .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // Send the positive button event back to the host activity
-                       mListener.onDialogPositiveClick(NoticeDialog.this);
+                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
                    }
                })
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // Send the negative button event back to the host activity
-                       mListener.onDialogPositiveClick(NoticeDialog.this);
+                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
                    }
                });
         return builder.create();
@@ -604,8 +601,6 @@
 
 
 
-
-
 <h2 id="ShowingADialog">Showing a Dialog</h2>
 
 <p>When you want to show your dialog, create an instance of your {@link
@@ -621,7 +616,7 @@
 
 <pre>
 public void confirmFireMissiles() {
-    DialogFragment newFragment = FireMissilesDialog.newInstance(this);
+    DialogFragment newFragment = new FireMissilesDialogFragment();
     newFragment.show(getSupportFragmentManager(), "missiles");
 }
 </pre>
@@ -653,7 +648,7 @@
 dialog or an embeddable fragment (using a layout named <code>purchase_items.xml</code>):</p>
 
 <pre>
-public class CustomLayoutDialog extends DialogFragment {
+public class CustomDialogFragment extends DialogFragment {
     /** The system calls this to get the DialogFragment's layout, regardless
         of whether it's being displayed as a dialog or an embedded fragment. */
     &#64;Override
@@ -683,7 +678,7 @@
 <pre>
 public void showDialog() {
     FragmentManager fragmentManager = getSupportFragmentManager();
-    CustomLayoutDialog newFragment = new CustomLayoutDialog();
+    CustomDialogFragment newFragment = new CustomDialogFragment();
     
     if (mIsLargeLayout) {
         // The device is using a large layout, so show the fragment as a dialog
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 69be317..448e3da 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -209,6 +209,9 @@
     }
 
     inline void allocateTexture(GLenum format, GLenum storage) {
+#if DEBUG_LAYERS
+        ALOGD("  Allocate layer: %dx%d", getWidth(), getHeight());
+#endif
         glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 406d5e9..b6be5b3 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -895,12 +895,6 @@
 void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
     float alpha = layer->getAlpha() / 255.0f;
 
-    mat4& transform = layer->getTransform();
-    if (!transform.isIdentity()) {
-        save(0);
-        mSnapshot->transform->multiply(transform);
-    }
-
     setupDraw();
     if (layer->getRenderTarget() == GL_TEXTURE_2D) {
         setupDrawWithTexture();
@@ -937,10 +931,6 @@
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 
     finishDrawTexture();
-
-    if (!transform.isIdentity()) {
-        restore();
-    }
 }
 
 void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
@@ -2792,12 +2782,24 @@
         return DrawGlInfo::kStatusDone;
     }
 
+    mat4* transform = NULL;
+    if (layer->isTextureLayer()) {
+        transform = &layer->getTransform();
+        if (!transform->isIdentity()) {
+            save(0);
+            mSnapshot->transform->multiply(*transform);
+        }
+    }
+
     Rect transformed;
     Rect clip;
     const bool rejected = quickRejectNoScissor(x, y,
             x + layer->layer.getWidth(), y + layer->layer.getHeight(), transformed, clip);
 
     if (rejected) {
+        if (transform && !transform->isIdentity()) {
+            restore();
+        }
         return DrawGlInfo::kStatusDone;
     }
 
@@ -2858,6 +2860,10 @@
         }
     }
 
+    if (transform && !transform->isIdentity()) {
+        restore();
+    }
+
     return DrawGlInfo::kStatusDrew;
 }
 
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index cb291ea..6871ee2 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -221,6 +221,18 @@
     /** @hide */
     public LocationRequest() { }
 
+    /** @hide */
+    public LocationRequest(LocationRequest src) {
+        mQuality = src.mQuality;
+        mInterval = src.mInterval;
+        mFastestInterval = src.mFastestInterval;
+        mExplicitFastestInterval = src.mExplicitFastestInterval;
+        mExpireAt = src.mExpireAt;
+        mNumUpdates = src.mNumUpdates;
+        mSmallestDisplacement = src.mSmallestDisplacement;
+        mProvider = src.mProvider;
+    }
+
     /**
      * Set the quality of the request.
      *
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index 4c57401..10b9064 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -18,7 +18,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.location.fused"
-        coreApp="true">
+        coreApp="true"
+        android:sharedUserId="android.uid.system">
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -39,7 +40,7 @@
            <intent-filter>
                <action android:name="com.android.location.service.FusedLocationProvider" />
            </intent-filter>
-           <meta-data android:name="version" android:value="1" />
+           <meta-data android:name="serviceVersion" android:value="0" />
         </service>
     </application>
 </manifest>
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 5a5769b..f7b1d78 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -21,6 +21,8 @@
 
     <ImageView
         android:id="@+id/brightness_icon"
+	    android:layout_width="wrap_content"
+	    android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:paddingRight="10dp"
         android:src="@drawable/ic_qs_brightness_auto_off"
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 0958f70..0a7dd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -109,7 +109,7 @@
         private WallpaperObserver mReceiver;
 
         Bitmap mBackground;
-        int mBackgroundWidth = -1, mBackgroundHeight = -1;
+        int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
         int mLastRotation = -1;
         float mXOffset;
         float mYOffset;
@@ -156,7 +156,7 @@
                 }
 
                 synchronized (mLock) {
-                    mBackgroundWidth = mBackgroundHeight = -1;
+                    mLastSurfaceWidth = mLastSurfaceHeight = -1;
                     mBackground = null;
                     mRedrawNeeded = true;
                     drawFrameLocked();
@@ -172,6 +172,9 @@
         public void trimMemory(int level) {
             if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
                     mBackground != null && mIsHwAccelerated) {
+                if (DEBUG) {
+                    Log.d(TAG, "trimMemory");
+                }
                 mBackground.recycle();
                 mBackground = null;
                 mWallpaperManager.forgetLoadedWallpaper();
@@ -286,13 +289,13 @@
         @Override
         public void onSurfaceDestroyed(SurfaceHolder holder) {
             super.onSurfaceDestroyed(holder);
-            mBackgroundWidth = mBackgroundHeight = -1;
+            mLastSurfaceWidth = mLastSurfaceHeight = -1;
         }
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
             super.onSurfaceCreated(holder);
-            mBackgroundWidth = mBackgroundHeight = -1;
+            mLastSurfaceWidth = mLastSurfaceHeight = -1;
         }
 
         @Override
@@ -314,9 +317,9 @@
             final int dh = frame.height();
             int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
                     getDefaultDisplay().getRotation();
+            boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
 
-            boolean redrawNeeded = dw != mBackgroundWidth || dh != mBackgroundHeight ||
-                    newRotation != mLastRotation;
+            boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
             if (!redrawNeeded && !mOffsetsChanged) {
                 if (DEBUG) {
                     Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
@@ -327,21 +330,41 @@
             mLastRotation = newRotation;
 
             // Load bitmap if it is not yet loaded or if it was loaded at a different size
-            if (mBackground == null || dw != mBackgroundWidth || dw != mBackgroundHeight) {
+            if (mBackground == null || surfaceDimensionsChanged) {
                 if (DEBUG) {
-                    Log.d(TAG, "Reloading bitmap");
+                    Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
+                            mBackground + ", " +
+                            ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
+                            ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
+                            dw + ", " + dh);
                 }
-                mWallpaperManager.forgetLoadedWallpaper();
                 updateWallpaperLocked();
+                if (mBackground == null) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Unable to load bitmap");
+                    }
+                    return;
+                }
+                if (DEBUG) {
+                    if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
+                        Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
+                                dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
+                                mBackground.getHeight());
+                    }
+                }
             }
 
-            final int availw = dw - mBackgroundWidth;
-            final int availh = dh - mBackgroundHeight;
+            final int availw = dw - mBackground.getWidth();
+            final int availh = dh - mBackground.getHeight();
             int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
             int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
 
             mOffsetsChanged = false;
             mRedrawNeeded = false;
+            if (surfaceDimensionsChanged) {
+                mLastSurfaceWidth = dw;
+                mLastSurfaceHeight = dh;
+            }
             mLastXTranslation = xPixels;
             mLastYTranslation = yPixels;
             if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
@@ -374,9 +397,10 @@
 
         }
 
-        void updateWallpaperLocked() {
+        private void updateWallpaperLocked() {
             Throwable exception = null;
             try {
+                mWallpaperManager.forgetLoadedWallpaper(); // force reload
                 mBackground = mWallpaperManager.getBitmap();
             } catch (RuntimeException e) {
                 exception = e;
@@ -397,9 +421,6 @@
                     Log.w(TAG, "Unable reset to default wallpaper!", ex);
                 }
             }
-
-            mBackgroundWidth = mBackground != null ? mBackground.getWidth() : 0;
-            mBackgroundHeight = mBackground != null ? mBackground.getHeight() : 0;
         }
 
         private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
@@ -413,7 +434,8 @@
                     c.translate(x, y);
                     if (w < 0 || h < 0) {
                         c.save(Canvas.CLIP_SAVE_FLAG);
-                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
+                        c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(),
+                                Op.DIFFERENCE);
                         c.drawColor(0xff000000);
                         c.restore();
                     }
@@ -429,8 +451,8 @@
         private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
             if (!initGL(sh)) return false;
 
-            final float right = left + mBackgroundWidth;
-            final float bottom = top + mBackgroundHeight;
+            final float right = left + mBackground.getWidth();
+            final float bottom = top + mBackground.getHeight();
 
             final Rect frame = sh.getSurfaceFrame();
             final Matrix4f ortho = new Matrix4f();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 57d2ed3..9f0bcf5 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -185,6 +185,16 @@
                 final Activity activity = (Activity) RecentsPanelView.this.getContext();
                 final SystemUIApplication app = (SystemUIApplication) activity.getApplication();
                 if (app.isWaitingForWindowAnimationStart()) {
+                    if (mItemToAnimateInWhenWindowAnimationIsFinished != null) {
+                        for (View v :
+                            new View[] { holder.iconView, holder.labelView, holder.calloutLine }) {
+                            if (v != null) {
+                                v.setAlpha(1f);
+                                v.setTranslationX(0f);
+                                v.setTranslationY(0f);
+                            }
+                        }
+                    }
                     mItemToAnimateInWhenWindowAnimationIsFinished = holder;
                     final int translation = -getResources().getDimensionPixelSize(
                             R.dimen.status_bar_recents_app_icon_translate_distance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a857eba..248a516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -35,7 +35,7 @@
     View mHandleView;
     int mFingers;
     PhoneStatusBar mStatusBar;
-    private boolean mFlipped;
+    boolean mOkToFlip;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -94,10 +94,10 @@
         if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
-                    mFlipped = false;
+                    mOkToFlip = getExpandedHeight() == 0;
                     break;
                 case MotionEvent.ACTION_POINTER_DOWN:
-                    if (!mFlipped) {
+                    if (mOkToFlip) {
                         float miny = event.getY(0);
                         float maxy = miny;
                         for (int i=1; i<event.getPointerCount(); i++) {
@@ -111,7 +111,7 @@
                             } else {
                                 mStatusBar.flipToSettings();
                             }
-                            mFlipped = true;
+                            mOkToFlip = false;
                         }
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 27a4db5..1c4dff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1487,6 +1487,7 @@
         mFlipSettingsView.setVisibility(View.VISIBLE);
         mSettingsButton.setVisibility(View.GONE);
         mScrollView.setVisibility(View.GONE);
+        mScrollView.setScaleX(0f);
         mNotificationButton.setVisibility(View.VISIBLE);
         mNotificationButton.setAlpha(1f);
         mClearButton.setVisibility(View.GONE);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 91d5eaa..24ce9bc 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -299,6 +299,7 @@
     int mCarDockRotation;
     int mDeskDockRotation;
     int mHdmiRotation;
+    boolean mHdmiRotationLock;
 
     int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
     int mUserRotation = Surface.ROTATION_0;
@@ -1035,11 +1036,14 @@
             mCanHideNavigationBar = false;
         }
 
+        // For demo purposes, allow the rotation of the HDMI display to be controlled.
+        // By default, HDMI locks rotation to landscape.
         if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
             mHdmiRotation = mPortraitRotation;
         } else {
             mHdmiRotation = mLandscapeRotation;
         }
+        mHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", true);
     }
 
     public void updateSettings() {
@@ -3873,7 +3877,7 @@
                 // enable 180 degree rotation while docked.
                 preferredRotation = mDeskDockEnablesAccelerometer
                         ? sensorRotation : mDeskDockRotation;
-            } else if (mHdmiPlugged) {
+            } else if (mHdmiPlugged && mHdmiRotationLock) {
                 // Ignore sensor when plugged into HDMI.
                 // Note that the dock orientation overrides the HDMI orientation.
                 preferredRotation = mHdmiRotation;
@@ -4538,5 +4542,7 @@
                 pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation);
         pw.print(prefix); pw.print("mPortraitRotation="); pw.print(mPortraitRotation);
                 pw.print(" mUpsideDownRotation="); pw.println(mUpsideDownRotation);
+        pw.print(prefix); pw.print("mHdmiRotation="); pw.print(mHdmiRotation);
+                pw.print(" mHdmiRotationLock="); pw.println(mHdmiRotationLock);
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 0ad2404..b66c883 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -43,7 +43,7 @@
 
 /**
  * Manages creating, showing, hiding and resetting the keyguard.  Calls back
- * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke
+ * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke
  * the wake lock and report that the keyguard is done, which is in turn,
  * reported to this class by the current {@link KeyguardViewBase}.
  */
@@ -233,6 +233,7 @@
 
         if (mScreenOn) {
             mKeyguardView.show();
+            mKeyguardView.requestFocus();
         }
     }
 
@@ -314,22 +315,25 @@
 
             // Caller should wait for this window to be shown before turning
             // on the screen.
-            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
-                // Keyguard may be in the process of being shown, but not yet
-                // updated with the window manager...  give it a chance to do so.
-                mKeyguardHost.post(new Runnable() {
-                    public void run() {
-                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
-                            showListener.onShown(mKeyguardHost.getWindowToken());
-                        } else {
-                            showListener.onShown(null);
+            if (showListener != null) {
+                if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                    // Keyguard may be in the process of being shown, but not yet
+                    // updated with the window manager...  give it a chance to do so.
+                    mKeyguardHost.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                                showListener.onShown(mKeyguardHost.getWindowToken());
+                            } else {
+                                showListener.onShown(null);
+                            }
                         }
-                    }
-                });
-            } else {
-                showListener.onShown(null);
+                    });
+                } else {
+                    showListener.onShown(null);
+                }
             }
-        } else {
+        } else if (showListener != null) {
             showListener.onShown(null);
         }
     }
@@ -356,10 +360,9 @@
         if (mKeyguardView != null) {
             mKeyguardView.wakeWhenReadyTq(keyCode);
             return true;
-        } else {
-            Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
-            return false;
         }
+        Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
+        return false;
     }
 
     /**
@@ -382,6 +385,7 @@
                 final KeyguardViewBase lastView = mKeyguardView;
                 mKeyguardView = null;
                 mKeyguardHost.postDelayed(new Runnable() {
+                    @Override
                     public void run() {
                         synchronized (KeyguardViewManager.this) {
                             lastView.cleanUp();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 92f9dfd..ceb0325 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -629,9 +629,7 @@
             mScreenOn = true;
             cancelDoKeyguardLaterLocked();
             if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
-            if (showListener != null) {
-                notifyScreenOnLocked(showListener);
-            }
+            notifyScreenOnLocked(showListener);
         }
         maybeSendUserPresentBroadcast();
     }
@@ -1365,7 +1363,7 @@
 
     /**
      * Handle message sent by {@link #verifyUnlock}
-     * @see #RESET
+     * @see #VERIFY_UNLOCK
      */
     private void handleVerifyUnlock() {
         synchronized (KeyguardViewMediator.this) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 9f01eca..f241c80 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1474,6 +1474,7 @@
             if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
             removeEverBackedUp(packageName);
             set.remove(packageName);
+            mPendingBackups.remove(packageName);
         }
     }
 
@@ -1625,6 +1626,7 @@
                         } catch (InterruptedException e) {
                             // just bail
                             if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
+                            mActivityManager.clearPendingBackup();
                             return null;
                         }
                     }
@@ -1632,6 +1634,7 @@
                     // if we timed out with no connect, abort and move on
                     if (mConnecting == true) {
                         Slog.w(TAG, "Timeout waiting for agent " + app);
+                        mActivityManager.clearPendingBackup();
                         return null;
                     }
                     if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 63eeeb3..37dee19 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -23,8 +23,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 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.ResolveInfo;
+import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.location.Address;
@@ -90,10 +93,13 @@
     private static final String WAKELOCK_KEY = TAG;
     private static final String THREAD_NAME = TAG;
 
-    private static final String ACCESS_FINE_LOCATION =
-            android.Manifest.permission.ACCESS_FINE_LOCATION;
-    private static final String ACCESS_COARSE_LOCATION =
-            android.Manifest.permission.ACCESS_COARSE_LOCATION;
+    // Location resolution level: no location data whatsoever
+    private static final int RESOLUTION_LEVEL_NONE = 0;
+    // Location resolution level: coarse location data only
+    private static final int RESOLUTION_LEVEL_COARSE = 1;
+    // Location resolution level: fine location data
+    private static final int RESOLUTION_LEVEL_FINE = 2;
+
     private static final String ACCESS_MOCK_LOCATION =
             android.Manifest.permission.ACCESS_MOCK_LOCATION;
     private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
@@ -246,6 +252,74 @@
         updateProvidersLocked();
     }
 
+    private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) {
+        PackageManager pm = mContext.getPackageManager();
+        String systemPackageName = mContext.getPackageName();
+        ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
+
+        List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser(
+                new Intent(FUSED_LOCATION_SERVICE_ACTION),
+                PackageManager.GET_META_DATA, mCurrentUserId);
+        for (ResolveInfo rInfo : rInfos) {
+            String packageName = rInfo.serviceInfo.packageName;
+
+            // Check that the signature is in the list of supported sigs. If it's not in
+            // this list the standard provider binding logic won't bind to it.
+            try {
+                PackageInfo pInfo;
+                pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+                if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) {
+                    Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION +
+                            ", but has wrong signature, ignoring");
+                    continue;
+                }
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "missing package: " + packageName);
+                continue;
+            }
+
+            // Get the version info
+            if (rInfo.serviceInfo.metaData == null) {
+                Log.w(TAG, "Found fused provider without metadata: " + packageName);
+                continue;
+            }
+
+            int version = rInfo.serviceInfo.metaData.getInt(
+                    ServiceWatcher.EXTRA_SERVICE_VERSION, -1);
+            if (version == 0) {
+                // This should be the fallback fused location provider.
+
+                // Make sure it's in the system partition.
+                if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                    if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName);
+                    continue;
+                }
+
+                // Check that the fallback is signed the same as the OS
+                // as a proxy for coreApp="true"
+                if (pm.checkSignatures(systemPackageName, packageName)
+                        != PackageManager.SIGNATURE_MATCH) {
+                    if (D) Log.d(TAG, "Fallback candidate not signed the same as system: "
+                            + packageName);
+                    continue;
+                }
+
+                // Found a valid fallback.
+                if (D) Log.d(TAG, "Found fallback provider: " + packageName);
+                return;
+            } else {
+                if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName);
+            }
+        }
+
+        throw new IllegalStateException("Unable to find a fused location provider that is in the "
+                + "system partition with version 0 and signed with the platform certificate. "
+                + "Such a package is needed to provide a default fused location provider in the "
+                + "event that no other fused location provider has been installed or is currently "
+                + "available. For example, coreOnly boot mode when decrypting the data "
+                + "partition. The fallback must also be marked coreApp=\"true\" in the manifest");
+    }
+
     private void loadProvidersLocked() {
         // create a passive location provider, which is always enabled
         PassiveProvider passiveProvider = new PassiveProvider(this);
@@ -275,14 +349,13 @@
         */
         Resources resources = mContext.getResources();
         ArrayList<String> providerPackageNames = new ArrayList<String>();
-        String[] pkgs1 = resources.getStringArray(
+        String[] pkgs = resources.getStringArray(
                 com.android.internal.R.array.config_locationProviderPackageNames);
-        String[] pkgs2 = resources.getStringArray(
-                com.android.internal.R.array.config_overlay_locationProviderPackageNames);
-        if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
-        if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
-        if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
-        if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
+        if (D) Log.d(TAG, "certificates for location providers pulled from: " +
+                Arrays.toString(pkgs));
+        if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
+
+        ensureFallbackFusedProviderPresentLocked(providerPackageNames);
 
         // bind to network provider
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
@@ -347,7 +420,7 @@
         final int mUid;  // uid of receiver
         final int mPid;  // pid of receiver
         final String mPackageName;  // package name of receiver
-        final String mPermission;  // best permission that receiver has
+        final int mAllowedResolutionLevel;  // resolution level allowed to receiver
 
         final ILocationListener mListener;
         final PendingIntent mPendingIntent;
@@ -366,7 +439,7 @@
             } else {
                 mKey = intent;
             }
-            mPermission = checkPermission();
+            mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
             mUid = uid;
             mPid = pid;
             mPackageName = packageName;
@@ -440,7 +513,7 @@
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
                         mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
-                                mPermission);
+                                getResolutionPermission(mAllowedResolutionLevel));
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -474,7 +547,7 @@
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
                         mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
-                                mPermission);
+                                getResolutionPermission(mAllowedResolutionLevel));
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -512,7 +585,7 @@
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
                         mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
-                                mPermission);
+                                getResolutionPermission(mAllowedResolutionLevel));
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -609,51 +682,76 @@
     }
 
     /**
-     * Returns the best permission available to the caller.
+     * Returns the permission string associated with the specified resolution level.
+     *
+     * @param resolutionLevel the resolution level
+     * @return the permission string
      */
-    private String getBestCallingPermission() {
-        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
-                PackageManager.PERMISSION_GRANTED) {
-            return ACCESS_FINE_LOCATION;
-        } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
-                PackageManager.PERMISSION_GRANTED) {
-            return ACCESS_COARSE_LOCATION;
+    private String getResolutionPermission(int resolutionLevel) {
+        switch (resolutionLevel) {
+            case RESOLUTION_LEVEL_FINE:
+                return android.Manifest.permission.ACCESS_FINE_LOCATION;
+            case RESOLUTION_LEVEL_COARSE:
+                return android.Manifest.permission.ACCESS_COARSE_LOCATION;
+            default:
+                return null;
         }
-        return null;
     }
 
     /**
-     * Throw SecurityException if caller has neither COARSE or FINE.
-     * Otherwise, return the best permission.
+     * Returns the resolution level allowed to the given PID/UID pair.
+     *
+     * @param pid the PID
+     * @param uid the UID
+     * @return resolution level allowed to the pid/uid pair
      */
-    private String checkPermission() {
-        String perm = getBestCallingPermission();
-        if (perm == null) {
-            throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
-                    " ACCESS_FINE_LOCATION permission");
+    private int getAllowedResolutionLevel(int pid, int uid) {
+        if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+                pid, uid) == PackageManager.PERMISSION_GRANTED) {
+            return RESOLUTION_LEVEL_FINE;
+        } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+                pid, uid) == PackageManager.PERMISSION_GRANTED) {
+            return RESOLUTION_LEVEL_COARSE;
+        } else {
+            return RESOLUTION_LEVEL_NONE;
         }
-        return perm;
     }
 
     /**
-     * Throw SecurityException if caller lacks permission to use Geofences.
+     * Returns the resolution level allowed to the caller
+     *
+     * @return resolution level allowed to caller
      */
-    private void checkGeofencePermission() {
-        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
-                PackageManager.PERMISSION_GRANTED) {
+    private int getCallerAllowedResolutionLevel() {
+        return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
+    }
+
+    /**
+     * Throw SecurityException if specified resolution level is insufficient to use geofences.
+     *
+     * @param allowedResolutionLevel resolution level allowed to caller
+     */
+    private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
+        if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
             throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
         }
     }
 
-    private String getMinimumPermissionForProvider(String provider) {
+    /**
+     * Return the minimum resolution level required to use the specified location provider.
+     *
+     * @param provider the name of the location provider
+     * @return minimum resolution level required for provider
+     */
+    private int getMinimumResolutionLevelForProviderUse(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider) ||
                 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
             // gps and passive providers require FINE permission
-            return ACCESS_FINE_LOCATION;
+            return RESOLUTION_LEVEL_FINE;
         } else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
                 LocationManager.FUSED_PROVIDER.equals(provider)) {
             // network and fused providers are ok with COARSE or FINE
-            return ACCESS_COARSE_LOCATION;
+            return RESOLUTION_LEVEL_COARSE;
         } else {
             // mock providers
             LocationProviderInterface lp = mMockProviders.get(provider);
@@ -662,41 +760,38 @@
                 if (properties != null) {
                     if (properties.mRequiresSatellite) {
                         // provider requiring satellites require FINE permission
-                        return ACCESS_FINE_LOCATION;
+                        return RESOLUTION_LEVEL_FINE;
                     } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
                         // provider requiring network and or cell require COARSE or FINE
-                        return ACCESS_COARSE_LOCATION;
+                        return RESOLUTION_LEVEL_COARSE;
                     }
                 }
             }
         }
-
-        return null;
+        return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
-    private boolean isPermissionSufficient(String perm, String minPerm) {
-        if (ACCESS_FINE_LOCATION.equals(minPerm)) {
-            return ACCESS_FINE_LOCATION.equals(perm);
-        } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) {
-            return ACCESS_FINE_LOCATION.equals(perm) ||
-                    ACCESS_COARSE_LOCATION.equals(perm);
-        } else {
-            return false;
-        }
-    }
-
-    private void checkPermissionForProvider(String perm, String provider) {
-        String minPerm = getMinimumPermissionForProvider(provider);
-        if (!isPermissionSufficient(perm, minPerm)) {
-            if (ACCESS_FINE_LOCATION.equals(minPerm)) {
-                throw new SecurityException("Location provider \"" + provider +
-                        "\" requires ACCESS_FINE_LOCATION permission.");
-            } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) {
-                throw new SecurityException("Location provider \"" + provider +
-                        "\" requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");                
-            } else {
-                throw new SecurityException("Insufficient permission for location provider \"" +
-                        provider + "\".");
+    /**
+     * Throw SecurityException if specified resolution level is insufficient to use the named
+     * location provider.
+     *
+     * @param allowedResolutionLevel resolution level allowed to caller
+     * @param providerName the name of the location provider
+     */
+    private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
+            String providerName) {
+        int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
+        if (allowedResolutionLevel < requiredResolutionLevel) {
+            switch (requiredResolutionLevel) {
+                case RESOLUTION_LEVEL_FINE:
+                    throw new SecurityException("\"" + providerName + "\" location provider " +
+                            "requires ACCESS_FINE_LOCATION permission.");
+                case RESOLUTION_LEVEL_COARSE:
+                    throw new SecurityException("\"" + providerName + "\" location provider " +
+                            "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");
+                default:
+                    throw new SecurityException("Insufficient permission for \"" + providerName +
+                            "\" location provider.");
             }
         }
     }
@@ -731,8 +826,8 @@
      */
     @Override
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         ArrayList<String> out;
-        String perm = getBestCallingPermission();
         int callingUserId = UserHandle.getCallingUserId();
         long identity = Binder.clearCallingIdentity();
         try {
@@ -743,7 +838,7 @@
                     if (LocationManager.FUSED_PROVIDER.equals(name)) {
                         continue;
                     }
-                    if (isPermissionSufficient(perm, getMinimumPermissionForProvider(name))) {
+                    if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
                         if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
                             continue;
                         }
@@ -803,8 +898,6 @@
 
     @Override
     public boolean providerMeetsCriteria(String provider, Criteria criteria) {
-        checkPermission();
-
         LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
@@ -1010,33 +1103,41 @@
         return receiver;
     }
 
-    private String checkPermissionAndRequest(LocationRequest request) {
-        String perm = getBestCallingPermission();
-        String provider = request.getProvider();
-        checkPermissionForProvider(perm, provider);
-
-        if (ACCESS_COARSE_LOCATION.equals(perm)) {
-            switch (request.getQuality()) {
+    /**
+     * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution
+     * and consistency requirements.
+     *
+     * @param request the LocationRequest from which to create a sanitized version
+     * @param shouldBeCoarse whether the sanitized version should be held to coarse resolution
+     * constraints
+     * @param fastestCoarseIntervalMS minimum interval allowed for coarse resolution
+     * @return a version of request that meets the given resolution and consistency requirements
+     * @hide
+     */
+    private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel) {
+        LocationRequest sanitizedRequest = new LocationRequest(request);
+        if (resolutionLevel < RESOLUTION_LEVEL_FINE) {
+            switch (sanitizedRequest.getQuality()) {
                 case LocationRequest.ACCURACY_FINE:
-                    request.setQuality(LocationRequest.ACCURACY_BLOCK);
+                    sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK);
                     break;
                 case LocationRequest.POWER_HIGH:
-                    request.setQuality(LocationRequest.POWER_LOW);
+                    sanitizedRequest.setQuality(LocationRequest.POWER_LOW);
                     break;
             }
             // throttle
-            if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
-                request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
+            if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+                sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
             }
-            if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
-                request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
+            if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+                sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
             }
         }
         // make getFastestInterval() the minimum of interval and fastest interval
-        if (request.getFastestInterval() > request.getInterval()) {
+        if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) {
             request.setFastestInterval(request.getInterval());
         }
-        return perm;
+        return sanitizedRequest;
     }
 
     private void checkPackageName(String packageName) {
@@ -1079,7 +1180,10 @@
             PendingIntent intent, String packageName) {
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         checkPackageName(packageName);
-        checkPermissionAndRequest(request);
+        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+        checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
+                request.getProvider());
+        LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -1089,7 +1193,7 @@
         long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
+                requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -1132,7 +1236,7 @@
     public void removeUpdates(ILocationListener listener, PendingIntent intent,
             String packageName) {
         checkPackageName(packageName);
-        checkPermission();
+
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
@@ -1188,8 +1292,11 @@
     public Location getLastLocation(LocationRequest request, String packageName) {
         if (D) Log.d(TAG, "getLastLocation: " + request);
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
-        String perm = checkPermissionAndRequest(request);
+        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkPackageName(packageName);
+        checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
+                request.getProvider());
+        // no need to sanitize this request, as only the provider name is used
 
         long identity = Binder.clearCallingIdentity();
         try {
@@ -1213,13 +1320,13 @@
                 if (location == null) {
                     return null;
                 }
-                if (ACCESS_FINE_LOCATION.equals(perm)) {
-                    return location;
-                } else {
+                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
                     Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
                     if (noGPSLocation != null) {
                         return mLocationFudger.getOrCreate(noGPSLocation);
                     }
+                } else {
+                    return location;
                 }
             }
             return null;
@@ -1232,18 +1339,21 @@
     public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
             String packageName) {
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
-        checkGeofencePermission();
-        checkPermissionAndRequest(request);
+        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+        checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
         checkPendingIntent(intent);
         checkPackageName(packageName);
+        checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
+                request.getProvider());
+        LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
 
-        if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
+        if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
 
         // geo-fence manager uses the public location API, need to clear identity
         int uid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
-            mGeofenceManager.addFence(request, geofence, intent, uid, packageName);
+            mGeofenceManager.addFence(sanitizedRequest, geofence, intent, uid, packageName);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1251,7 +1361,7 @@
 
     @Override
     public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
-        checkGeofencePermission();
+        checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel());
         checkPendingIntent(intent);
         checkPackageName(packageName);
 
@@ -1272,10 +1382,8 @@
         if (mGpsStatusProvider == null) {
             return false;
         }
-        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
-                PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
-        }
+        checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+                LocationManager.GPS_PROVIDER);
 
         try {
             mGpsStatusProvider.addGpsStatusListener(listener);
@@ -1303,8 +1411,9 @@
             // throw NullPointerException to remain compatible with previous implementation
             throw new NullPointerException();
         }
+        checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+                provider);
 
-        checkPermission();
         // and check for ACCESS_LOCATION_EXTRA_COMMANDS
         if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
                 != PackageManager.PERMISSION_GRANTED)) {
@@ -1344,7 +1453,8 @@
           return null;
         }
 
-        checkPermissionForProvider(getBestCallingPermission(), provider);
+        checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+                provider);
 
         LocationProviderInterface p;
         synchronized (mLock) {
@@ -1357,7 +1467,8 @@
 
     @Override
     public boolean isProviderEnabled(String provider) {
-        checkPermissionForProvider(getBestCallingPermission(), provider);
+        checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+                provider);
         if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
 
         long identity = Binder.clearCallingIdentity();
@@ -1522,10 +1633,10 @@
             }
 
             Location notifyLocation = null;
-            if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
-                notifyLocation = lastLocation;  // use fine location
+            if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                notifyLocation = coarseLocation;  // use coarse location
             } else {
-                notifyLocation = coarseLocation;  // use coarse location if available
+                notifyLocation = lastLocation;  // use fine location
             }
             if (notifyLocation != null) {
                 Location lastLoc = r.mLastFixBroadcast;
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index 5598b0a..2e7c6d1 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -43,7 +43,7 @@
  */
 public class ServiceWatcher implements ServiceConnection {
     private static final boolean D = false;
-    private static final String EXTRA_VERSION = "version";
+    public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
 
     private final String mTag;
     private final Context mContext;
@@ -58,9 +58,27 @@
     // all fields below synchronized on mLock
     private IBinder mBinder;   // connected service
     private String mPackageName;  // current best package
-    private int mVersion;  // current best version
+    private int mVersion = Integer.MIN_VALUE;  // current best version
     private int mCurrentUserId;
 
+    public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
+            List<String> initialPackageNames) {
+        PackageManager pm = context.getPackageManager();
+        ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>();
+        for (int i = 0, size = initialPackageNames.size(); i < size; i++) {
+            String pkg = initialPackageNames.get(i);
+            try {
+                HashSet<Signature> set = new HashSet<Signature>();
+                Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
+                set.addAll(Arrays.asList(sigs));
+                sigSets.add(set);
+            } catch (NameNotFoundException e) {
+                Log.w("ServiceWatcher", pkg + " not found");
+            }
+        }
+        return sigSets;
+    }
+
     public ServiceWatcher(Context context, String logTag, String action,
             List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
         mContext = context;
@@ -71,20 +89,7 @@
         mHandler = handler;
         mCurrentUserId = userId;
 
-        mSignatureSets = new ArrayList<HashSet<Signature>>();
-        for (int i=0; i < initialPackageNames.size(); i++) {
-            String pkg = initialPackageNames.get(i);
-            HashSet<Signature> set = new HashSet<Signature>();
-            try {
-                Signature[] sigs =
-                        mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
-                set.addAll(Arrays.asList(sigs));
-                mSignatureSets.add(set);
-            } catch (NameNotFoundException e) {
-                Log.w(logTag, pkg + " not found");
-            }
-        }
-
+        mSignatureSets = getSignatureSets(context, initialPackageNames);
     }
 
     public boolean start() {
@@ -132,15 +137,16 @@
             // check version
             int version = 0;
             if (rInfo.serviceInfo.metaData != null) {
-                version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0);
+                version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0);
             }
+
             if (version > mVersion) {
                 bestVersion = version;
                 bestPackage = packageName;
             }
         }
 
-        if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s",
+        if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
                 (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
                 rInfos.size(),
                 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
@@ -174,7 +180,8 @@
                 | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
     }
 
-    private boolean isSignatureMatch(Signature[] signatures) {
+    public static boolean isSignatureMatch(Signature[] signatures,
+            List<HashSet<Signature>> sigSets) {
         if (signatures == null) return false;
 
         // build hashset of input to test against
@@ -184,7 +191,7 @@
         }
 
         // test input against each of the signature sets
-        for (HashSet<Signature> referenceSet : mSignatureSets) {
+        for (HashSet<Signature> referenceSet : sigSets) {
             if (referenceSet.equals(inputSet)) {
                 return true;
             }
@@ -192,6 +199,10 @@
         return false;
     }
 
+    private boolean isSignatureMatch(Signature[] signatures) {
+        return isSignatureMatch(signatures, mSignatureSets);
+    }
+
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         /**
          * Called when package has been reinstalled
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7132e1e..1737876a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -11119,8 +11119,8 @@
     // 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) Slog.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode);
-        enforceCallingPermission("android.permission.BACKUP", "startBackupAgent");
+        if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + app + " mode=" + backupMode);
+        enforceCallingPermission("android.permission.BACKUP", "bindBackupAgent");
 
         synchronized(this) {
             // !!! TODO: currently no check here that we're already bound
@@ -11181,6 +11181,17 @@
         return true;
     }
 
+    @Override
+    public void clearPendingBackup() {
+        if (DEBUG_BACKUP) Slog.v(TAG, "clearPendingBackup");
+        enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup");
+
+        synchronized (this) {
+            mBackupTarget = null;
+            mBackupAppName = null;
+        }
+    }
+
     // A backup agent has just come up                    
     public void backupAgentCreated(String agentPackageName, IBinder agent) {
         if (DEBUG_BACKUP) Slog.v(TAG, "backupAgentCreated: " + agentPackageName
@@ -11217,32 +11228,34 @@
         }
 
         synchronized(this) {
-            if (mBackupAppName == null) {
-                Slog.w(TAG, "Unbinding backup agent with no active backup");
-                return;
-            }
-
-            if (!mBackupAppName.equals(appInfo.packageName)) {
-                Slog.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,
-                            compatibilityInfoForPackageLocked(appInfo));
-                } catch (Exception e) {
-                    Slog.e(TAG, "Exception when unbinding backup agent:");
-                    e.printStackTrace();
+            try {
+                if (mBackupAppName == null) {
+                    Slog.w(TAG, "Unbinding backup agent with no active backup");
+                    return;
                 }
+
+                if (!mBackupAppName.equals(appInfo.packageName)) {
+                    Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
+                    return;
+                }
+
+                // Not backing this app up any more; reset its OOM adjustment
+                final ProcessRecord proc = mBackupTarget.app;
+                updateOomAdjLocked(proc);
+
+                // If the app crashed during backup, 'thread' will be null here
+                if (proc.thread != null) {
+                    try {
+                        proc.thread.scheduleDestroyBackupAgent(appInfo,
+                                compatibilityInfoForPackageLocked(appInfo));
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Exception when unbinding backup agent:");
+                        e.printStackTrace();
+                    }
+                }
+            } finally {
+                mBackupTarget = null;
+                mBackupAppName = null;
             }
         }
     }
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index b4dab86..e76bf44 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import android.util.DisplayMetrics;
+import android.view.Surface;
 
 import libcore.util.Objects;
 
@@ -31,11 +32,21 @@
     public static final int FLAG_DEFAULT_DISPLAY = 1 << 0;
 
     /**
-     * Flag: Indicates that this display device can rotate to show contents in a
-     * different orientation.  Otherwise the rotation is assumed to be fixed in the
-     * natural orientation and the display manager should transform the content to fit.
+     * Flag: Indicates that the orientation of this display device is coupled to the
+     * rotation of its associated logical display.
+     * <p>
+     * This flag should be applied to the default display to indicate that the user
+     * physically rotates the display when content is presented in a different orientation.
+     * The display manager will apply a coordinate transformation assuming that the
+     * physical orientation of the display matches the logical orientation of its content.
+     * </p><p>
+     * The flag should not be set when the display device is mounted in a fixed orientation
+     * such as on a desk.  The display manager will apply a coordinate transformation
+     * such as a scale and translation to letterbox or pillarbox format under the
+     * assumption that the physical orientation of the display is invariant.
+     * </p>
      */
-    public static final int FLAG_SUPPORTS_ROTATION = 1 << 1;
+    public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 1;
 
     /**
      * Flag: Indicates that this display device has secure video output, such as HDCP.
@@ -116,6 +127,17 @@
      */
     public int touch;
 
+    /**
+     * The additional rotation to apply to all content presented on the display device
+     * relative to its physical coordinate system.  Default is {@link Surface#ROTATION_0}.
+     * <p>
+     * This field can be used to compensate for the fact that the display has been
+     * physically rotated relative to its natural orientation such as an HDMI monitor
+     * that has been mounted sideways to appear to be portrait rather than landscape.
+     * </p>
+     */
+    public int rotation = Surface.ROTATION_0;
+
     public void setAssumedDensityForExternalDisplay(int width, int height) {
         densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
         // Technically, these values should be smaller than the apparent density
@@ -139,7 +161,8 @@
                 && xDpi == other.xDpi
                 && yDpi == other.yDpi
                 && flags == other.flags
-                && touch == other.touch;
+                && touch == other.touch
+                && rotation == other.rotation;
     }
 
     @Override
@@ -157,14 +180,18 @@
         yDpi = other.yDpi;
         flags = other.flags;
         touch = other.touch;
+        rotation = other.rotation;
     }
 
     // For debugging purposes
     @Override
     public String toString() {
-        return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
+        return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", "
+                + refreshRate + " fps, "
                 + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
-                + ", touch " + touchToString(touch) + flagsToString(flags) + "}";
+                + ", touch " + touchToString(touch) + flagsToString(flags)
+                + ", rotation " + rotation
+                + "}";
     }
 
     private static String touchToString(int touch) {
@@ -185,8 +212,8 @@
         if ((flags & FLAG_DEFAULT_DISPLAY) != 0) {
             msg.append(", FLAG_DEFAULT_DISPLAY");
         }
-        if ((flags & FLAG_SUPPORTS_ROTATION) != 0) {
-            msg.append(", FLAG_SUPPORTS_ROTATION");
+        if ((flags & FLAG_ROTATES_WITH_CONTENT) != 0) {
+            msg.append(", FLAG_ROTATES_WITH_CONTENT");
         }
         if ((flags & FLAG_SECURE) != 0) {
             msg.append(", FLAG_SECURE");
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 93896af..e58a0a5cb 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -127,6 +127,13 @@
     // services should be started.  This option may disable certain display adapters.
     public boolean mOnlyCore;
 
+    // True if the display manager service should pretend there is only one display
+    // and only tell applications about the existence of the default logical display.
+    // The display manager can still mirror content to secondary displays but applications
+    // cannot present unique content on those displays.
+    // Used for demonstration purposes only.
+    private final boolean mSingleDisplayDemoMode;
+
     // All callback records indexed by calling process id.
     public final SparseArray<CallbackRecord> mCallbacks =
             new SparseArray<CallbackRecord>();
@@ -182,6 +189,7 @@
         mHandler = new DisplayManagerHandler(mainHandler.getLooper());
         mUiHandler = uiHandler;
         mDisplayAdapterListener = new DisplayAdapterListener();
+        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
 
         mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
     }
@@ -631,6 +639,12 @@
             isDefault = false;
         }
 
+        if (!isDefault && mSingleDisplayDemoMode) {
+            Slog.i(TAG, "Not creating a logical display for a secondary display "
+                    + " because single display demo mode is enabled: " + deviceInfo);
+            return;
+        }
+
         final int displayId = assignDisplayIdLocked(isDefault);
         final int layerStack = assignLayerStackLocked(displayId);
 
@@ -857,6 +871,7 @@
             pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
             pw.println("  mDefaultViewport=" + mDefaultViewport);
             pw.println("  mExternalTouchViewport=" + mExternalTouchViewport);
+            pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
 
             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
             ipw.increaseIndent();
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index d780006..fe38d7f 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.util.SparseArray;
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
@@ -135,7 +136,7 @@
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_built_in_display_name);
                     mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
-                            | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION;
+                            | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
                     mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
                     mInfo.xDpi = mPhys.xDpi;
                     mInfo.yDpi = mPhys.yDpi;
@@ -145,6 +146,12 @@
                             com.android.internal.R.string.display_manager_hdmi_display_name);
                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
                     mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
+
+                    // For demonstration purposes, allow rotation of the external display.
+                    // In the future we might allow the user to configure this directly.
+                    if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
+                        mInfo.rotation = Surface.ROTATION_270;
+                    }
                 }
             }
             return mInfo;
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index 680662e..aa7ea82 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -241,10 +241,13 @@
         // is rotated when the contents of the logical display are rendered.
         int orientation = Surface.ROTATION_0;
         if (device == mPrimaryDisplayDevice
-                && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) {
+                && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) {
             orientation = displayInfo.rotation;
         }
 
+        // Apply the physical rotation of the display device itself.
+        orientation = (orientation + displayDeviceInfo.rotation) % 4;
+
         // Set the frame.
         // The frame specifies the rotated physical coordinates into which the viewport
         // is mapped.  We need to take care to preserve the aspect ratio of the viewport.
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index c441b02..66eac88 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -16,17 +16,28 @@
 
 package com.android.server.display;
 
+import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
 import android.media.RemoteDisplay;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Slog;
 import android.view.Surface;
 
@@ -52,8 +63,18 @@
 
     private static final boolean DEBUG = false;
 
+    private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1;
+    private static final int MSG_UPDATE_NOTIFICATION = 2;
+
+    private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT";
+
+    private final WifiDisplayHandler mHandler;
     private final PersistentDataStore mPersistentDataStore;
     private final boolean mSupportsProtectedBuffers;
+    private final NotificationManager mNotificationManager;
+
+    private final PendingIntent mSettingsPendingIntent;
+    private final PendingIntent mDisconnectPendingIntent;
 
     private WifiDisplayController mDisplayController;
     private WifiDisplayDevice mDisplayDevice;
@@ -67,14 +88,32 @@
     private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY;
 
     private boolean mPendingStatusChangeBroadcast;
+    private boolean mPendingNotificationUpdate;
 
     public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener,
             PersistentDataStore persistentDataStore) {
         super(syncRoot, context, handler, listener, TAG);
+        mHandler = new WifiDisplayHandler(handler.getLooper());
         mPersistentDataStore = persistentDataStore;
         mSupportsProtectedBuffers = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers);
+        mNotificationManager = (NotificationManager)context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+
+        Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+        settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mSettingsPendingIntent = PendingIntent.getActivityAsUser(
+                context, 0, settingsIntent, 0, null, UserHandle.CURRENT);
+
+        Intent disconnectIntent = new Intent(ACTION_DISCONNECT);
+        mDisconnectPendingIntent = PendingIntent.getBroadcastAsUser(
+                context, 0, disconnectIntent, 0, UserHandle.CURRENT);
+
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                new IntentFilter(ACTION_DISCONNECT), null, mHandler);
     }
 
     @Override
@@ -89,6 +128,7 @@
         pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays));
         pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays));
         pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
+        pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
         pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
 
         // Try to dump the controller state.
@@ -266,6 +306,8 @@
         mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
                 refreshRate, deviceFlags, surface);
         sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
+
+        scheduleUpdateNotificationLocked();
     }
 
     private void handleDisconnectLocked() {
@@ -273,6 +315,8 @@
             mDisplayDevice.clearSurfaceLocked();
             sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
             mDisplayDevice = null;
+
+            scheduleUpdateNotificationLocked();
         }
     }
 
@@ -280,28 +324,81 @@
         mCurrentStatus = null;
         if (!mPendingStatusChangeBroadcast) {
             mPendingStatusChangeBroadcast = true;
-            getHandler().post(mStatusChangeBroadcast);
+            mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
         }
     }
 
-    private final Runnable mStatusChangeBroadcast = new Runnable() {
-        @Override
-        public void run() {
-            final Intent intent;
-            synchronized (getSyncRoot()) {
-                if (!mPendingStatusChangeBroadcast) {
-                    return;
-                }
+    private void scheduleUpdateNotificationLocked() {
+        if (!mPendingNotificationUpdate) {
+            mPendingNotificationUpdate = true;
+            mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION);
+        }
+    }
 
-                mPendingStatusChangeBroadcast = false;
-                intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
-                        getWifiDisplayStatusLocked());
+    // Runs on the handler.
+    private void handleSendStatusChangeBroadcast() {
+        final Intent intent;
+        synchronized (getSyncRoot()) {
+            if (!mPendingStatusChangeBroadcast) {
+                return;
             }
 
-            // Send protected broadcast about wifi display status to registered receivers.
-            getContext().sendBroadcast(intent);
+            mPendingStatusChangeBroadcast = false;
+            intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
+                    getWifiDisplayStatusLocked());
+        }
+
+        // Send protected broadcast about wifi display status to registered receivers.
+        getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    // Runs on the handler.
+    private void handleUpdateNotification() {
+        final boolean isConnected;
+        synchronized (getSyncRoot()) {
+            if (!mPendingNotificationUpdate) {
+                return;
+            }
+
+            mPendingNotificationUpdate = false;
+            isConnected = (mDisplayDevice != null);
+        }
+
+        mNotificationManager.cancelAsUser(null,
+                R.string.wifi_display_notification_title, UserHandle.ALL);
+
+        if (isConnected) {
+            Context context = getContext();
+
+            Resources r = context.getResources();
+            Notification notification = new Notification.Builder(context)
+                    .setContentTitle(r.getString(
+                            R.string.wifi_display_notification_title))
+                    .setContentText(r.getString(
+                            R.string.wifi_display_notification_message))
+                    .setContentIntent(mSettingsPendingIntent)
+                    .setSmallIcon(R.drawable.ic_notify_wifidisplay)
+                    .setOngoing(true)
+                    .addAction(android.R.drawable.ic_menu_close_clear_cancel,
+                            r.getString(R.string.wifi_display_notification_disconnect),
+                            mDisconnectPendingIntent)
+                    .build();
+            mNotificationManager.notifyAsUser(null,
+                    R.string.wifi_display_notification_title,
+                    notification, UserHandle.ALL);
+        }
+    }
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(ACTION_DISCONNECT)) {
+                synchronized (getSyncRoot()) {
+                    requestDisconnectLocked();
+                }
+            }
         }
     };
 
@@ -454,4 +551,23 @@
             return mInfo;
         }
     }
+
+    private final class WifiDisplayHandler extends Handler {
+        public WifiDisplayHandler(Looper looper) {
+            super(looper, null, true /*async*/);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SEND_STATUS_CHANGE_BROADCAST:
+                    handleSendStatusChangeBroadcast();
+                    break;
+
+                case MSG_UPDATE_NOTIFICATION:
+                    handleUpdateNotification();
+                    break;
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 77e6c03..072dd33 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -62,6 +62,8 @@
 
     private static final String LOG_TAG = "UserManagerService";
 
+    private static final boolean DBG = false;
+
     private static final String TAG_NAME = "name";
     private static final String ATTR_FLAGS = "flags";
     private static final String ATTR_ICON_PATH = "icon";
@@ -97,6 +99,9 @@
     private int[] mUserIds;
     private boolean mGuestEnabled;
     private int mNextSerialNumber;
+    // This resets on a reboot. Otherwise it keeps incrementing so that user ids are
+    // not reused in quick succession
+    private int mNextUserId = MIN_USER_ID;
 
     private static UserManagerService sInstance;
 
@@ -199,7 +204,8 @@
      */
     private UserInfo getUserInfoLocked(int userId) {
         UserInfo ui = mUsers.get(userId);
-        if (ui != null && ui.partial) {
+        // If it is partial and not in the process of being removed, return as unknown user.
+        if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) {
             Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
             return null;
         }
@@ -668,6 +674,7 @@
                     long now = System.currentTimeMillis();
                     userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
                     userInfo.partial = true;
+                    Environment.getUserSystemDirectory(userInfo.id).mkdirs();
                     mUsers.put(userId, userInfo);
                     writeUserListLocked();
                     writeUserLocked(userInfo);
@@ -709,7 +716,7 @@
             user.partial = true;
             writeUserLocked(user);
         }
-
+        if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
         int res;
         try {
             res = ActivityManagerNative.getDefault().stopUser(userHandle,
@@ -730,12 +737,13 @@
     }
 
     void finishRemoveUser(int userHandle) {
+        if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
         synchronized (mInstallLock) {
             synchronized (mPackagesLock) {
                 removeUserStateLocked(userHandle);
             }
         }
-
+        if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast");
         // Let other services shutdown any activity
         long ident = Binder.clearCallingIdentity();
         try {
@@ -804,10 +812,11 @@
                 num++;
             }
         }
-        int[] newUsers = new int[num];
+        final int[] newUsers = new int[num];
+        int n = 0;
         for (int i = 0; i < mUsers.size(); i++) {
             if (!mUsers.valueAt(i).partial) {
-                newUsers[i] = mUsers.keyAt(i);
+                newUsers[n++] = mUsers.keyAt(i);
             }
         }
         mUserIds = newUsers;
@@ -840,13 +849,14 @@
      */
     private int getNextAvailableIdLocked() {
         synchronized (mPackagesLock) {
-            int i = MIN_USER_ID;
+            int i = mNextUserId;
             while (i < Integer.MAX_VALUE) {
                 if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) {
                     break;
                 }
                 i++;
             }
+            mNextUserId = i + 1;
             return i;
         }
     }
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 4e692a2..b94bceb 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -107,6 +107,8 @@
     private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9;
     // Dirty bit: screen on blocker state became held or unheld
     private static final int DIRTY_SCREEN_ON_BLOCKER_RELEASED = 1 << 10;
+    // Dirty bit: dock state changed
+    private static final int DIRTY_DOCK_STATE = 1 << 11;
 
     // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
     // The screen should be off or in the process of being turned off by the display controller.
@@ -269,6 +271,9 @@
     // draining faster than it is charging and the user activity timeout has expired.
     private int mBatteryLevelWhenDreamStarted;
 
+    // The current dock state.
+    private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
     // True if the device should wake up when plugged or unplugged.
     private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
 
@@ -281,6 +286,9 @@
     // True if dreams should be activated on sleep.
     private boolean mDreamsActivateOnSleepSetting;
 
+    // True if dreams should be activated on dock.
+    private boolean mDreamsActivateOnDockSetting;
+
     // The screen off timeout setting value in milliseconds.
     private int mScreenOffTimeoutSetting;
 
@@ -440,6 +448,10 @@
             filter.addAction(Intent.ACTION_USER_SWITCHED);
             mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
 
+            filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_DOCK_EVENT);
+            mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
+
             // Register for settings changes.
             final ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -448,6 +460,9 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP),
                     false, mSettingsObserver, UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.SCREEN_OFF_TIMEOUT),
                     false, mSettingsObserver, UserHandle.USER_ALL);
@@ -487,6 +502,9 @@
         mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver,
                 Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0,
                 UserHandle.USER_CURRENT) != 0);
+        mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver,
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 0,
+                UserHandle.USER_CURRENT) != 0);
         mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
                 Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
                 UserHandle.USER_CURRENT);
@@ -1339,13 +1357,14 @@
     private boolean updateWakefulnessLocked(int dirty) {
         boolean changed = false;
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
-                | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE)) != 0) {
+                | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
+                | DIRTY_DOCK_STATE)) != 0) {
             if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
                 if (DEBUG_SPEW) {
                     Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
                 }
                 final long time = SystemClock.uptimeMillis();
-                if (mDreamsActivateOnSleepSetting) {
+                if (shouldNapAtBedTimeLocked()) {
                     changed = napNoUpdateLocked(time);
                 } else {
                     changed = goToSleepNoUpdateLocked(time,
@@ -1357,6 +1376,16 @@
     }
 
     /**
+     * Returns true if the device should automatically nap and start dreaming when the user
+     * activity timeout has expired and it's bedtime.
+     */
+    private boolean shouldNapAtBedTimeLocked() {
+        return mDreamsActivateOnSleepSetting
+                || (mDreamsActivateOnDockSetting
+                        && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+    }
+
+    /**
      * Returns true if the device should go to sleep now.
      * Also used when exiting a dream to determine whether we should go back
      * to being fully awake or else go to sleep for good.
@@ -2124,6 +2153,7 @@
             pw.println("  mPlugType=" + mPlugType);
             pw.println("  mBatteryLevel=" + mBatteryLevel);
             pw.println("  mBatteryLevelWhenDreamStarted=" + mBatteryLevelWhenDreamStarted);
+            pw.println("  mDockState=" + mDockState);
             pw.println("  mStayOn=" + mStayOn);
             pw.println("  mProximityPositive=" + mProximityPositive);
             pw.println("  mBootCompleted=" + mBootCompleted);
@@ -2149,6 +2179,7 @@
             pw.println("  mDreamsSupportedConfig=" + mDreamsSupportedConfig);
             pw.println("  mDreamsEnabledSetting=" + mDreamsEnabledSetting);
             pw.println("  mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
+            pw.println("  mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
             pw.println("  mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
             pw.println("  mMaximumScreenOffTimeoutFromDeviceAdmin="
                     + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
@@ -2267,6 +2298,21 @@
         }
     }
 
+    private final class DockReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                if (mDockState != dockState) {
+                    mDockState = dockState;
+                    mDirty |= DIRTY_DOCK_STATE;
+                    updatePowerStateLocked();
+                }
+            }
+        }
+    }
+
     private final class SettingsObserver extends ContentObserver {
         public SettingsObserver(Handler handler) {
             super(handler);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index fa450ae..e74b6db 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2420,6 +2420,7 @@
         final WindowList windows = win.getWindowList();
         windows.remove(win);
         mPendingRemove.remove(win);
+        mResizingWindows.remove(win);
         mWindowsChanged = true;
         if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 196bf2e..fd76fc8d 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -359,8 +359,12 @@
     static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
 
     /* P2p commands */
+    /* We are ok with no response here since we wont do much with it anyway */
     public static final int CMD_ENABLE_P2P                = BASE + 131;
-    public static final int CMD_DISABLE_P2P               = BASE + 132;
+    /* In order to shut down supplicant cleanly, we wait till p2p has
+     * been disabled */
+    public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
+    public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
 
     private static final int CONNECT_MODE   = 1;
     private static final int SCAN_ONLY_MODE = 2;
@@ -458,6 +462,11 @@
     private State mDriverStartingState = new DriverStartingState();
     /* Driver started */
     private State mDriverStartedState = new DriverStartedState();
+    /* Wait until p2p is disabled
+     * This is a special state which is entered right after we exit out of DriverStartedState
+     * before transitioning to another state.
+     */
+    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
     /* Driver stopping */
     private State mDriverStoppingState = new DriverStoppingState();
     /* Driver stopped */
@@ -699,6 +708,7 @@
                         addState(mDisconnectingState, mConnectModeState);
                         addState(mDisconnectedState, mConnectModeState);
                         addState(mWpsRunningState, mConnectModeState);
+                addState(mWaitForP2pDisableState, mSupplicantStartedState);
                 addState(mDriverStoppingState, mSupplicantStartedState);
                 addState(mDriverStoppedState, mSupplicantStartedState);
             addState(mSupplicantStoppingState, mDefaultState);
@@ -2433,7 +2443,11 @@
             WifiConfiguration config;
             switch(message.what) {
                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
-                    transitionTo(mSupplicantStoppingState);
+                    if (mP2pSupported) {
+                        transitionTo(mWaitForP2pDisableState);
+                    } else {
+                        transitionTo(mSupplicantStoppingState);
+                    }
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
                     loge("Connection lost, restart supplicant");
@@ -2443,7 +2457,11 @@
                     handleNetworkDisconnect();
                     sendSupplicantConnectionChangedBroadcast(false);
                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
-                    transitionTo(mDriverLoadedState);
+                    if (mP2pSupported) {
+                        transitionTo(mWaitForP2pDisableState);
+                    } else {
+                        transitionTo(mDriverLoadedState);
+                    }
                     sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     break;
                 case WifiMonitor.SCAN_RESULTS_EVENT:
@@ -2838,8 +2856,12 @@
                     }
                     mWakeLock.acquire();
                     mWifiNative.stopDriver();
-                    transitionTo(mDriverStoppingState);
                     mWakeLock.release();
+                    if (mP2pSupported) {
+                        transitionTo(mWaitForP2pDisableState);
+                    } else {
+                        transitionTo(mDriverStoppingState);
+                    }
                     break;
                 case CMD_START_PACKET_FILTERING:
                     if (message.arg1 == MULTICAST_V6) {
@@ -2885,8 +2907,63 @@
             mIsRunning = false;
             updateBatteryWorkSource(null);
             mScanResults = new ArrayList<ScanResult>();
+        }
+    }
 
-            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P);
+    class WaitForP2pDisableState extends State {
+        private State mTransitionToState;
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+            switch (getCurrentMessage().what) {
+                case WifiMonitor.SUP_DISCONNECTION_EVENT:
+                    mTransitionToState = mDriverLoadedState;
+                    break;
+                case CMD_DELAYED_STOP_DRIVER:
+                    mTransitionToState = mDriverStoppingState;
+                    break;
+                case CMD_STOP_SUPPLICANT:
+                    mTransitionToState = mSupplicantStoppingState;
+                    break;
+                default:
+                    mTransitionToState = mDriverStoppingState;
+                    break;
+            }
+            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString() + "\n");
+            switch(message.what) {
+                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
+                    transitionTo(mTransitionToState);
+                    break;
+                /* Defer wifi start/shut and driver commands */
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_SET_FREQUENCY_BAND:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                case CMD_START_SCAN:
+                case CMD_DISCONNECT:
+                case CMD_REASSOCIATE:
+                case CMD_RECONNECT:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
         }
     }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 4b90901..7f32431 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -134,6 +134,9 @@
     private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupCreatingTimeoutIndex = 0;
 
+    private static final int DISABLE_P2P_WAIT_TIME_MS = 5 * 1000;
+    private static int mDisableP2pTimeoutIndex = 0;
+
     /* Set a two minute discover timeout to avoid STA scans from being blocked */
     private static final int DISCOVER_TIMEOUT_S = 120;
 
@@ -153,6 +156,8 @@
     private static final int DROP_WIFI_USER_ACCEPT          =   BASE + 4;
     /* User wants to keep his wifi connection and drop p2p */
     private static final int DROP_WIFI_USER_REJECT          =   BASE + 5;
+    /* Delayed message to timeout p2p disable */
+    public static final int DISABLE_P2P_TIMED_OUT           =   BASE + 6;
 
 
     /* Commands to the WifiStateMachine */
@@ -574,19 +579,25 @@
                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
                 case WifiMonitor.P2P_FIND_STOPPED_EVENT:
                 case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
-                case WifiStateMachine.CMD_ENABLE_P2P:
-                case WifiStateMachine.CMD_DISABLE_P2P:
                 case PEER_CONNECTION_USER_ACCEPT:
                 case PEER_CONNECTION_USER_REJECT:
                 case DISCONNECT_WIFI_RESPONSE:
                 case DROP_WIFI_USER_ACCEPT:
                 case DROP_WIFI_USER_REJECT:
                 case GROUP_CREATING_TIMED_OUT:
+                case DISABLE_P2P_TIMED_OUT:
                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                 case DhcpStateMachine.CMD_ON_QUIT:
                 case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
                     break;
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                    // Enable is lazy and has no response
+                    break;
+                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
+                    // If we end up handling in default, p2p is not enabled
+                    mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
+                    break;
                     /* unexpected group created, remove */
                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
                     mGroup = (WifiP2pGroup) message.obj;
@@ -689,6 +700,13 @@
 
     class P2pDisablingState extends State {
         @Override
+        public void enter() {
+            if (DBG) logd(getName());
+            sendMessageDelayed(obtainMessage(DISABLE_P2P_TIMED_OUT,
+                    ++mDisableP2pTimeoutIndex, 0), DISABLE_P2P_WAIT_TIME_MS);
+        }
+
+        @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
@@ -697,14 +715,25 @@
                     transitionTo(mP2pDisabledState);
                     break;
                 case WifiStateMachine.CMD_ENABLE_P2P:
-                case WifiStateMachine.CMD_DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
                     deferMessage(message);
                     break;
+                case DISABLE_P2P_TIMED_OUT:
+                    if (mGroupCreatingTimeoutIndex == message.arg1) {
+                        loge("P2p disable timed out");
+                        transitionTo(mP2pDisabledState);
+                    }
+                    break;
                 default:
                     return NOT_HANDLED;
             }
             return HANDLED;
         }
+
+        @Override
+        public void exit() {
+            mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
+        }
     }
 
     class P2pDisabledState extends State {
@@ -728,9 +757,6 @@
                     mWifiMonitor.startMonitoring();
                     transitionTo(mP2pEnablingState);
                     break;
-                case WifiStateMachine.CMD_DISABLE_P2P:
-                    //Nothing to do
-                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -757,7 +783,7 @@
                     transitionTo(mP2pDisabledState);
                     break;
                 case WifiStateMachine.CMD_ENABLE_P2P:
-                case WifiStateMachine.CMD_DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
                     deferMessage(message);
                     break;
                 default:
@@ -788,7 +814,7 @@
                 case WifiStateMachine.CMD_ENABLE_P2P:
                     //Nothing to do
                     break;
-                case WifiStateMachine.CMD_DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
                     if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
 
@@ -1534,7 +1560,7 @@
                     }
                     // Do the regular device lost handling
                     return NOT_HANDLED;
-                case WifiStateMachine.CMD_DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P_REQ:
                     sendMessage(WifiP2pManager.REMOVE_GROUP);
                     deferMessage(message);
                     break;