am 76d4d5d1: (-s ours) am c550a6fa: Merge "docs: Add Studio 1.3.2 release notes and download links" into mnc-dev

* commit '76d4d5d19dc4548c90d73fd74174bf4e21674011':
diff --git a/Android.mk b/Android.mk
index 7df1bd7..6f560ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -90,6 +90,7 @@
 	core/java/android/app/IWallpaperManager.aidl \
 	core/java/android/app/IWallpaperManagerCallback.aidl \
 	core/java/android/app/admin/IDevicePolicyManager.aidl \
+	core/java/android/app/trust/IStrongAuthTracker.aidl \
 	core/java/android/app/trust/ITrustManager.aidl \
 	core/java/android/app/trust/ITrustListener.aidl \
 	core/java/android/app/backup/IBackupManager.aidl \
@@ -172,6 +173,7 @@
 	core/java/android/hardware/input/IInputManager.aidl \
 	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
 	core/java/android/hardware/location/IActivityRecognitionHardware.aidl \
+	core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \
 	core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \
 	core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl \
 	core/java/android/hardware/location/IFusedLocationHardware.aidl \
diff --git a/api/system-current.txt b/api/system-current.txt
index 3c88e59..f464292 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -130,6 +130,7 @@
     field public static final java.lang.String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
     field public static final java.lang.String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
+    field public static final java.lang.String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
     field public static final java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
     field public static final java.lang.String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
@@ -32715,17 +32716,22 @@
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
+    field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
     field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+    field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
     field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
+    field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
+    field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -33425,6 +33431,7 @@
     field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+    field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
     field public static final int CALL_STATE_IDLE = 0; // 0x0
     field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
     field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -33441,11 +33448,15 @@
     field public static final int DATA_CONNECTING = 1; // 0x1
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
+    field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
     field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
+    field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+    field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
     field public static final java.lang.String EXTRA_STATE = "state";
     field public static final java.lang.String EXTRA_STATE_IDLE;
     field public static final java.lang.String EXTRA_STATE_OFFHOOK;
     field public static final java.lang.String EXTRA_STATE_RINGING;
+    field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
     field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index fba462b..8f361ce 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -556,15 +556,10 @@
 
     mZip->endIteration(cookie);
 
-    // clear screen
     glShadeModel(GL_FLAT);
     glDisable(GL_DITHER);
     glDisable(GL_SCISSOR_TEST);
     glDisable(GL_BLEND);
-    glClearColor(0,0,0,1);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    eglSwapBuffers(mDisplay, mSurface);
 
     glBindTexture(GL_TEXTURE_2D, 0);
     glEnable(GL_TEXTURE_2D);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 9f385fe..58c3a9c 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -983,6 +983,7 @@
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
         }
         return "Unknown link state: " + state;
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f3f2428..9381327 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4607,7 +4607,7 @@
          * Size value for use with {@link #setCustomSizePreset} to show this notification with
          * default sizing.
          * <p>For custom display notifications created using {@link #setDisplayIntent},
-         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
+         * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
          * on their content.
          */
         public static final int SIZE_DEFAULT = 0;
diff --git a/core/java/android/app/trust/IStrongAuthTracker.aidl b/core/java/android/app/trust/IStrongAuthTracker.aidl
new file mode 100644
index 0000000..36c71bf
--- /dev/null
+++ b/core/java/android/app/trust/IStrongAuthTracker.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+package android.app.trust;
+
+/**
+ * Private API to be notified about strong auth changes
+ *
+ * {@hide}
+ */
+oneway interface IStrongAuthTracker {
+    void onStrongAuthRequiredChanged(int strongAuthRequired, int userId);
+}
\ No newline at end of file
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 32951d9..2dea545 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -26,11 +26,9 @@
 interface ITrustManager {
     void reportUnlockAttempt(boolean successful, int userId);
     void reportEnabledTrustAgentsChanged(int userId);
-    void reportRequireCredentialEntry(int userId);
     void registerTrustListener(in ITrustListener trustListener);
     void unregisterTrustListener(in ITrustListener trustListener);
     void reportKeyguardShowingChanged();
     boolean isDeviceLocked(int userId);
     boolean isDeviceSecure(int userId);
-    boolean hasUserAuthenticatedSinceBoot(int userId);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 8cab565..aff69f0 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,13 +16,19 @@
 
 package android.app.trust;
 
+import android.annotation.IntDef;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseIntArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * See {@link com.android.server.trust.TrustManagerService}
@@ -73,21 +79,6 @@
     }
 
     /**
-     * Reports that trust is disabled until credentials have been entered for user {@param userId}.
-     *
-     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
-     *
-     * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
-     */
-    public void reportRequireCredentialEntry(int userId) {
-        try {
-            mService.reportRequireCredentialEntry(userId);
-        } catch (RemoteException e) {
-            onError(e);
-        }
-    }
-
-    /**
      * Reports that the visibility of the keyguard has changed.
      *
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
@@ -147,23 +138,6 @@
         }
     }
 
-    /**
-     * Checks whether the specified user has been authenticated since the last boot.
-     *
-     * @param userId the user id of the user to check for
-     * @return true if the user has authenticated since boot, false otherwise
-     *
-     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
-     */
-    public boolean hasUserAuthenticatedSinceBoot(int userId) {
-        try {
-            return mService.hasUserAuthenticatedSinceBoot(userId);
-        } catch (RemoteException e) {
-            onError(e);
-            return false;
-        }
-    }
-
     private void onError(Exception e) {
         Log.e(TAG, "Error while calling TrustManagerService", e);
     }
diff --git a/core/java/android/hardware/ICameraServiceProxy.aidl b/core/java/android/hardware/ICameraServiceProxy.aidl
index 0bb24bc..0e654d5 100644
--- a/core/java/android/hardware/ICameraServiceProxy.aidl
+++ b/core/java/android/hardware/ICameraServiceProxy.aidl
@@ -19,6 +19,8 @@
 /**
  * Binder interface for the camera service proxy running in system_server.
  *
+ * Keep in sync with frameworks/av/include/camera/ICameraServiceProxy.h
+ *
  * @hide
  */
 interface ICameraServiceProxy
@@ -27,4 +29,9 @@
      * Ping the service proxy to update the valid users for the camera service.
      */
     oneway void pingForUserUpdate();
+
+    /**
+     * Update the status of a camera device
+     */
+     oneway void notifyCameraState(String cameraId, int newCameraState);
 }
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 46ffe36..766868d 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -138,6 +138,48 @@
      */
     public abstract void prepare(@NonNull Surface surface) throws CameraAccessException;
 
+    /**
+     * <p>Pre-allocate at most maxCount buffers for an output Surface.</p>
+     *
+     * <p>Like the {@link #prepare(Surface)} method, this method can be used to allocate output
+     * buffers for a given Surface.  However, while the {@link #prepare(Surface)} method allocates
+     * the maximum possible buffer count, this method allocates at most maxCount buffers.</p>
+     *
+     * <p>If maxCount is greater than the possible maximum count (which is the sum of the buffer
+     * count requested by the creator of the Surface and the count requested by the camera device),
+     * only the possible maximum count is allocated, in which case the function acts exactly like
+     * {@link #prepare(Surface)}.</p>
+     *
+     * <p>The restrictions on when this method can be called are the same as for
+     * {@link #prepare(Surface)}.</p>
+     *
+     * <p>Repeated calls to this method are allowed, and a mix of {@link #prepare(Surface)} and
+     * this method is also allowed. Note that after the first call to {@link #prepare(Surface)},
+     * subsequent calls to either prepare method are effectively no-ops.  In addition, this method
+     * is not additive in terms of buffer count.  This means calling it twice with maxCount = 2
+     * will only allocate 2 buffers, not 4 (assuming the possible maximum is at least 2); to
+     * allocate two buffers on the first call and two on the second, the application needs to call
+     * prepare with prepare(surface, 2) and prepare(surface, 4).</p>
+     *
+     * @param maxCount the buffer count to try to allocate. If this is greater than the possible
+     *                 maximum for this output, the possible maximum is allocated instead. If
+     *                 maxCount buffers are already allocated, then prepare will do nothing.
+     * @param surface the output Surface for which buffers should be pre-allocated.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error.
+     * @throws IllegalStateException if this session is no longer active, either because the
+     *                               session was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException if the Surface is invalid, not part of this Session,
+     *                                  or has already been used as a target of a CaptureRequest in
+     *                                  this session or immediately prior sessions without an
+     *                                  intervening tearDown call.
+     *
+     * @hide
+     */
+    public abstract void prepare(int maxCount, @NonNull Surface surface)
+            throws CameraAccessException;
 
     /**
      * <p>Free all buffers allocated for an output Surface.</p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 4a71aa0..20b0be1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -480,8 +480,11 @@
      * the configurations in the tables below are also guaranteed for creating a reprocessable
      * capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing.
      * However, not all output targets used to create a reprocessable session may be used in a
-     * {@link CaptureRequest} simultaneously. The guaranteed output targets that can be included
-     * in a {@link CaptureRequest} simultaneously are listed in the tables under
+     * {@link CaptureRequest} simultaneously. For devices that support only 1 output target in a
+     * reprocess {@link CaptureRequest}, submitting a reprocess {@link CaptureRequest} with multiple
+     * output targets will result in a {@link CaptureFailure}. For devices that support multiple
+     * output targets in a reprocess {@link CaptureRequest}, the guaranteed output targets that can
+     * be included in a {@link CaptureRequest} simultaneously are listed in the tables under
      * {@link #createCaptureSession createCaptureSession}. For example, with a FULL-capability
      * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} {@code == }
      * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) device that supports PRIVATE
@@ -532,8 +535,6 @@
      * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td></td><td id="rb"></td> <td>High-resolution ZSL in-app video processing with regular preview.</td> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
-     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td> <td>High-resolution ZSL in-app video processing and video snapshot with regular preview.</td> </tr>
-     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input ZSL in-app processing with regular preview.</td> </tr>
      * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL still capture and in-app processing.</td> </tr>
      * </table><br>
      * </p>
@@ -711,7 +712,9 @@
      * provide input images to camera device via {@link android.media.ImageWriter#queueInputImage}.
      * The application must use the capture result of one of those output images to create a
      * reprocess capture request so that the camera device can use the information to achieve
-     * optimal reprocess image quality.
+     * optimal reprocess image quality. For camera devices that support only 1 output
+     * {@link Surface}, submitting a reprocess {@link CaptureRequest} with multiple
+     * output targets will result in a {@link CaptureFailure}.
      *
      * @param inputResult The capture result of the output image or one of the output images used
      *                       to generate the reprocess input image for this capture request.
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 7cb3673..c9c9abc 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -102,4 +102,6 @@
     int prepare(int streamId);
 
     int tearDown(int streamId);
+
+    int prepare2(int maxCount, int streamId);
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index d325c77..5573896 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -146,6 +146,11 @@
     }
 
     @Override
+    public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+        mDeviceImpl.prepare(maxCount, surface);
+    }
+
+    @Override
     public void tearDown(Surface surface) throws CameraAccessException {
         mDeviceImpl.tearDown(surface);
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index a920e2b..8cd1da5 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -169,6 +169,11 @@
     }
 
     @Override
+    public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+        mSessionImpl.prepare(maxCount, surface);
+    }
+
+    @Override
     public void tearDown(Surface surface) throws CameraAccessException {
         mSessionImpl.tearDown(surface);
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 91d623e..6e02df1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -679,6 +679,33 @@
         }
     }
 
+    public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+        if (surface == null) throw new IllegalArgumentException("Surface is null");
+        if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " +
+                maxCount);
+
+        synchronized(mInterfaceLock) {
+            int streamId = -1;
+            for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+                if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
+                    streamId = mConfiguredOutputs.keyAt(i);
+                    break;
+                }
+            }
+            if (streamId == -1) {
+                throw new IllegalArgumentException("Surface is not part of this session");
+            }
+            try {
+                mRemoteDevice.prepare2(maxCount, streamId);
+            } catch (CameraRuntimeException e) {
+                throw e.asChecked();
+            } catch (RemoteException e) {
+                // impossible
+                return;
+            }
+        }
+    }
+
     public void tearDown(Surface surface) throws CameraAccessException {
         if (surface == null) throw new IllegalArgumentException("Surface is null");
 
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index e20eaa7..6b8e113 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -636,6 +636,11 @@
         return CameraBinderDecorator.NO_ERROR;
     }
 
+    public int prepare2(int maxCount, int streamId) {
+        // We don't support this in LEGACY mode.
+        return prepare(streamId);
+    }
+
     public int tearDown(int streamId) {
         if (DEBUG) {
             Log.d(TAG, "tearDown called.");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 7aa9787..a04cdce 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,7 +19,9 @@
 
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.utils.HashCodeHelpers;
+import android.hardware.camera2.utils.SurfaceUtils;
 import android.util.Log;
+import android.util.Size;
 import android.view.Surface;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -66,9 +68,7 @@
      *
      */
     public OutputConfiguration(Surface surface) {
-        checkNotNull(surface, "Surface must not be null");
-        mSurface = surface;
-        mRotation = ROTATION_0;
+        this(surface, ROTATION_0);
     }
 
     /**
@@ -94,6 +94,9 @@
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
         mSurface = surface;
         mRotation = rotation;
+        mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
+        mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
+        mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
     }
 
     /**
@@ -106,6 +109,9 @@
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
         mSurface = surface;
         mRotation = rotation;
+        mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
+        mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
+        mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
     }
 
     /**
@@ -163,8 +169,9 @@
     /**
      * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
      *
-     * <p>Two output configurations are only equal if and only if the underlying surface and
-     * all other configuration parameters are equal. </p>
+     * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
+     * properties (width, height, format, dataspace) when the output configurations are created,
+     * and all other configuration parameters are equal. </p>
      *
      * @return {@code true} if the objects were equal, {@code false} otherwise
      */
@@ -176,7 +183,11 @@
             return true;
         } else if (obj instanceof OutputConfiguration) {
             final OutputConfiguration other = (OutputConfiguration) obj;
-            return (mSurface == other.mSurface && mRotation == other.mRotation);
+            return mSurface == other.mSurface &&
+                   mRotation == other.mRotation &&
+                   mConfiguredSize.equals(other.mConfiguredSize) &&
+                   mConfiguredFormat == other.mConfiguredFormat &&
+                   mConfiguredDataspace == other.mConfiguredDataspace;
         }
         return false;
     }
@@ -192,4 +203,9 @@
     private static final String TAG = "OutputConfiguration";
     private final Surface mSurface;
     private final int mRotation;
+
+    // The size, format, and dataspace of the surface when OutputConfiguration is created.
+    private final Size mConfiguredSize;
+    private final int mConfiguredFormat;
+    private final int mConfiguredDataspace;
 }
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index e71e49f..b8d6960 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1290,7 +1290,7 @@
         for (StreamConfiguration config : configurations) {
             int fmt = config.getFormat();
             if (fmt == format && config.isOutput() == output) {
-                if (output) {
+                if (output && mListHighResolution) {
                     // Filter slow high-res output formats; include for
                     // highRes, remove for !highRes
                     long duration = 0;
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 6e29989..7cff11b 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -668,6 +668,25 @@
         return 0;
     }
 
+    /**
+     * Reset the lockout timer when asked to do so by keyguard.
+     *
+     * @param token an opaque token returned by password confirmation.
+     *
+     * @hide
+     */
+    public void resetTimeout(byte[] token) {
+        if (mService != null) {
+            try {
+                mService.resetTimeout(token);
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
+            }
+        } else {
+            Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+        }
+    }
+
     private class MyHandler extends Handler {
         private MyHandler(Context context) {
             super(context.getMainLooper());
@@ -677,6 +696,7 @@
             super(looper);
         }
 
+        @Override
         public void handleMessage(android.os.Message msg) {
             switch(msg.what) {
                 case MSG_ENROLL_RESULT:
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 5e233b8..3356354 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -68,4 +68,7 @@
 
     // Gets the authenticator ID for fingerprint
     long getAuthenticatorId(String opPackageName);
+
+    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetTimeout(in byte [] cryptoToken);
 }
diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java
index 5d3953a..8acd1ff 100644
--- a/core/java/android/hardware/location/ActivityRecognitionHardware.java
+++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java
@@ -30,20 +30,34 @@
  * @hide
  */
 public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
-    private static final String TAG = "ActivityRecognitionHardware";
+    private static final String TAG = "ActivityRecognitionHW";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+            + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
+
     private static final int INVALID_ACTIVITY_TYPE = -1;
     private static final int NATIVE_SUCCESS_RESULT = 0;
+    private static final int EVENT_TYPE_DISABLED = 0;
+    private static final int EVENT_TYPE_ENABLED = 1;
 
-    private static ActivityRecognitionHardware sSingletonInstance = null;
+    /**
+     * Contains the number of supported Event Types.
+     *
+     * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
+     *       com.android.location.provider.ActivityRecognitionProvider
+     */
+    private static final int EVENT_TYPE_COUNT = 3;
+
+    private static ActivityRecognitionHardware sSingletonInstance;
     private static final Object sSingletonInstanceLock = new Object();
 
     private final Context mContext;
+    private final int mSupportedActivitiesCount;
     private final String[] mSupportedActivities;
-
-    private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks =
-            new RemoteCallbackList<IActivityRecognitionHardwareSink>();
+    private final int[][] mSupportedActivitiesEnabledEvents;
+    private final SinkList mSinks = new SinkList();
 
     private static class Event {
         public int activity;
@@ -56,6 +70,8 @@
 
         mContext = context;
         mSupportedActivities = fetchSupportedActivities();
+        mSupportedActivitiesCount = mSupportedActivities.length;
+        mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
     }
 
     public static ActivityRecognitionHardware getInstance(Context context) {
@@ -107,7 +123,11 @@
         }
 
         int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
-        return result == NATIVE_SUCCESS_RESULT;
+        if (result == NATIVE_SUCCESS_RESULT) {
+            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -120,7 +140,11 @@
         }
 
         int result = nativeDisableActivityEvent(activityType, eventType);
-        return result == NATIVE_SUCCESS_RESULT;
+        if (result == NATIVE_SUCCESS_RESULT) {
+            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -135,7 +159,7 @@
      */
     private void onActivityChanged(Event[] events) {
         if (events == null || events.length == 0) {
-            Log.d(TAG, "No events to broadcast for onActivityChanged.");
+            if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
             return;
         }
 
@@ -161,7 +185,6 @@
             }
         }
         mSinks.finishBroadcast();
-
     }
 
     private String getActivityName(int activityType) {
@@ -193,10 +216,7 @@
     }
 
     private void checkPermissions() {
-        String message = String.format(
-                "Permission '%s' not granted to access ActivityRecognitionHardware",
-                HARDWARE_PERMISSION);
-        mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
+        mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
     }
 
     private String[] fetchSupportedActivities() {
@@ -208,6 +228,39 @@
         return new String[0];
     }
 
+    private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
+        @Override
+        public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
+            int callbackCount = mSinks.getRegisteredCallbackCount();
+            if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
+            if (callbackCount != 0) {
+                return;
+            }
+            // currently there is only one client for this, so if all its sinks have died, we clean
+            // up after them, this ensures that the AR HAL is not out of sink
+            for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
+                for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
+                    disableActivityEventIfEnabled(activity, event);
+                }
+            }
+        }
+
+        private void disableActivityEventIfEnabled(int activityType, int eventType) {
+            if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
+                return;
+            }
+
+            int result = nativeDisableActivityEvent(activityType, eventType);
+            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+            String message = String.format(
+                    "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
+                    activityType,
+                    eventType,
+                    result);
+            Log.e(TAG, message);
+        }
+    }
+
     // native bindings
     static { nativeClassInit(); }
 
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
new file mode 100644
index 0000000..d2c3d75
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/license/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.IActivityRecognitionHardware;
+
+/**
+ * Activity Recognition Hardware client interface.
+ * This interface can be used to receive interfaces to implementations of
+ * {@link IActivityRecognitionHardware}.
+ *
+ * @hide
+ */
+interface IActivityRecognitionHardwareClient {
+    /**
+     * Hardware Activity-Recognition availability event.
+     *
+     * @param isSupported whether the platform has hardware support for the feature
+     * @param instance the available instance to provide access to the feature
+     */
+    void onAvailabilityChanged(in boolean isSupported, in IActivityRecognitionHardware instance);
+}
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
index 0507f52..12e3117 100644
--- a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
@@ -22,6 +22,8 @@
  * Activity Recognition Hardware watcher. This interface can be used to receive interfaces to
  * implementations of {@link IActivityRecognitionHardware}.
  *
+ * @deprecated use {@link IActivityRecognitionHardwareClient} instead.
+
  * @hide
  */
 interface IActivityRecognitionHardwareWatcher {
@@ -29,4 +31,4 @@
      * Hardware Activity-Recognition availability event.
      */
     void onInstanceChanged(in IActivityRecognitionHardware instance);
-}
\ No newline at end of file
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ff6dd32..a4e6219 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1179,6 +1179,142 @@
         return true;
     }
 
+    /** @hide */
+    public static class PacketKeepaliveCallback {
+        /** The requested keepalive was successfully started. */
+        public void onStarted() {}
+        /** The keepalive was successfully stopped. */
+        public void onStopped() {}
+        /** An error occurred. */
+        public void onError(int error) {}
+    }
+
+    /**
+     * Allows applications to request that the system periodically send specific packets on their
+     * behalf, using hardware offload to save battery power.
+     *
+     * To request that the system send keepalives, call one of the methods that return a
+     * {@link ConnectivityManager.PacketKeepalive} object, such as {@link #startNattKeepalive},
+     * passing in a non-null callback. If the callback is successfully started, the callback's
+     * {@code onStarted} method will be called. If an error occurs, {@code onError} will be called,
+     * specifying one of the {@code ERROR_*} constants in this class.
+     *
+     * To stop an existing keepalive, call {@link stop}. The system will call {@code onStopped} if
+     * the operation was successfull or {@code onError} if an error occurred.
+     *
+     * @hide
+     */
+    public class PacketKeepalive {
+
+        private static final String TAG = "PacketKeepalive";
+
+        /** @hide */
+        public static final int SUCCESS = 0;
+
+        /** @hide */
+        public static final int NO_KEEPALIVE = -1;
+
+        /** @hide */
+        public static final int BINDER_DIED = -10;
+
+        /** The specified {@code Network} is not connected. */
+        public static final int ERROR_INVALID_NETWORK = -20;
+        /** The specified IP addresses are invalid. For example, the specified source IP address is
+          * not configured on the specified {@code Network}. */
+        public static final int ERROR_INVALID_IP_ADDRESS = -21;
+        /** The requested port is invalid. */
+        public static final int ERROR_INVALID_PORT = -22;
+        /** The packet length is invalid (e.g., too long). */
+        public static final int ERROR_INVALID_LENGTH = -23;
+        /** The packet transmission interval is invalid (e.g., too short). */
+        public static final int ERROR_INVALID_INTERVAL = -24;
+
+        /** The hardware does not support this request. */
+        public static final int ERROR_HARDWARE_UNSUPPORTED = -30;
+
+        public static final int NATT_PORT = 4500;
+
+        private final Network mNetwork;
+        private final PacketKeepaliveCallback mCallback;
+        private final Looper mLooper;
+        private final Messenger mMessenger;
+
+        private volatile Integer mSlot;
+
+        void stopLooper() {
+            mLooper.quit();
+        }
+
+        public void stop() {
+            try {
+                mService.stopKeepalive(mNetwork, mSlot);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error stopping packet keepalive: ", e);
+                stopLooper();
+            }
+        }
+
+        private PacketKeepalive(Network network, PacketKeepaliveCallback callback) {
+            checkNotNull(network, "network cannot be null");
+            checkNotNull(callback, "callback cannot be null");
+            mNetwork = network;
+            mCallback = callback;
+            HandlerThread thread = new HandlerThread(TAG);
+            thread.start();
+            mLooper = thread.getLooper();
+            mMessenger = new Messenger(new Handler(mLooper) {
+                @Override
+                public void handleMessage(Message message) {
+                    switch (message.what) {
+                        case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+                            int error = message.arg2;
+                            try {
+                                if (error == SUCCESS) {
+                                    if (mSlot == null) {
+                                        mSlot = message.arg1;
+                                        mCallback.onStarted();
+                                    } else {
+                                        mSlot = null;
+                                        stopLooper();
+                                        mCallback.onStopped();
+                                    }
+                                } else {
+                                    stopLooper();
+                                    mCallback.onError(error);
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "Exception in keepalive callback(" + error + ")", e);
+                            }
+                            break;
+                        default:
+                            Log.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
+                            break;
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Starts an IPsec NAT-T keepalive packet with the specified parameters.
+     *
+     * @hide
+     */
+    public PacketKeepalive startNattKeepalive(
+            Network network, int intervalSeconds, PacketKeepaliveCallback callback,
+            InetAddress srcAddr, int srcPort, InetAddress dstAddr) {
+        final PacketKeepalive k = new PacketKeepalive(network, callback);
+        try {
+            mService.startNattKeepalive(network, intervalSeconds, k.mMessenger, new Binder(),
+                    srcAddr.getHostAddress(), srcPort, dstAddr.getHostAddress());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error starting packet keepalive: ", e);
+            k.stopLooper();
+            return null;
+        }
+        return k;
+    }
+
     /**
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface. An attempt to add a route that
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 46c28a6..d4dd669 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -160,4 +160,9 @@
     boolean setUnderlyingNetworksForVpn(in Network[] networks);
 
     void factoryReset();
+
+    void startNattKeepalive(in Network network, int intervalSeconds, in Messenger messenger,
+            in IBinder binder, String srcAddr, int srcPort, String dstAddr);
+
+    void stopKeepalive(in Network network, int slot);
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 7f5f377..9e639e8 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -48,6 +48,9 @@
     /** Snooze limit on policy matching given template. */
     void snoozeLimit(in NetworkTemplate template);
 
+    /** Snooze warning on policy matching given template. */
+    void snoozeWarning(in NetworkTemplate template);
+
     /** Control if background data is restricted system-wide. */
     void setRestrictBackground(boolean restrictBackground);
     boolean getRestrictBackground();
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 808a882..20c2168 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -25,6 +26,7 @@
 
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
+import android.net.ConnectivityManager.PacketKeepalive;
 
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -143,17 +145,60 @@
      */
     public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
 
-    /** Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
+    /**
+     * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
      * the underlying network connection for updated bandwidth information.
      */
     public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
 
     /**
+     * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
+     * periodically on the given interval.
+     *
+     *   arg1 = the slot number of the keepalive to start
+     *   arg2 = interval in seconds
+     *   obj = KeepalivePacketData object describing the data to be sent
+     *
+     * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+     */
+    public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11;
+
+    /**
+     * Requests that the specified keepalive packet be stopped.
+     *
+     * arg1 = slot number of the keepalive to stop.
+     *
+     * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+     */
+    public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12;
+
+    /**
+     * Sent by the NetworkAgent to ConnectivityService to provide status on a packet keepalive
+     * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or an asynchronous
+     * error notification.
+     *
+     * This is also sent by KeepaliveTracker to the app's ConnectivityManager.PacketKeepalive to
+     * so that the app's PacketKeepaliveCallback methods can be called.
+     *
+     * arg1 = slot number of the keepalive
+     * arg2 = error code
+     */
+    public static final int EVENT_PACKET_KEEPALIVE = BASE + 13;
+
+    /**
+     * Sent by ConnectivityService to inform this network transport of signal strength thresholds
+     * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+     *
+     *   obj = int[] describing signal strength thresholds.
+     */
+    public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
+
+    /**
      * Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
      * automatically reconnecting to this network (e.g. via autojoin).  Happens
      * when user selects "No" option on the "Stay connected?" dialog box.
      */
-    public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 11;
+    public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
 
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
@@ -249,6 +294,27 @@
                 saveAcceptUnvalidated(msg.arg1 != 0);
                 break;
             }
+            case CMD_START_PACKET_KEEPALIVE: {
+                startPacketKeepalive(msg);
+                break;
+            }
+            case CMD_STOP_PACKET_KEEPALIVE: {
+                stopPacketKeepalive(msg);
+                break;
+            }
+
+            case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+                ArrayList<Integer> thresholds =
+                        ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+                // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+                // rather than convert to int[].
+                int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+                for (int i = 0; i < intThresholds.length; i++) {
+                    intThresholds[i] = thresholds.get(i);
+                }
+                setSignalStrengthThresholds(intThresholds);
+                break;
+            }
             case CMD_PREVENT_AUTOMATIC_RECONNECT: {
                 preventAutomaticReconnect();
                 break;
@@ -257,13 +323,27 @@
     }
 
     private void queueOrSendMessage(int what, Object obj) {
+        queueOrSendMessage(what, 0, 0, obj);
+    }
+
+    private void queueOrSendMessage(int what, int arg1, int arg2) {
+        queueOrSendMessage(what, arg1, arg2, null);
+    }
+
+    private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        queueOrSendMessage(msg);
+    }
+
+    private void queueOrSendMessage(Message msg) {
         synchronized (mPreConnectedQueue) {
             if (mAsyncChannel != null) {
-                mAsyncChannel.sendMessage(what, obj);
+                mAsyncChannel.sendMessage(msg);
             } else {
-                Message msg = Message.obtain();
-                msg.what = what;
-                msg.obj = obj;
                 mPreConnectedQueue.add(msg);
             }
         }
@@ -378,6 +458,34 @@
     }
 
     /**
+     * Requests that the network hardware send the specified packet at the specified interval.
+     */
+    protected void startPacketKeepalive(Message msg) {
+        onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+    }
+
+    /**
+     * Requests that the network hardware send the specified packet at the specified interval.
+     */
+    protected void stopPacketKeepalive(Message msg) {
+        onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+    }
+
+    /**
+     * Called by the network when a packet keepalive event occurs.
+     */
+    public void onPacketKeepaliveEvent(int slot, int reason) {
+        queueOrSendMessage(EVENT_PACKET_KEEPALIVE, slot, reason);
+    }
+
+    /**
+     * Called by ConnectivityService to inform this network transport of signal strength thresholds
+     * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+     */
+    protected void setSignalStrengthThresholds(int[] thresholds) {
+    }
+
+    /**
      * Called when the user asks to not stay connected to this network because it was found to not
      * provide Internet access.  Usually followed by call to {@code unwanted}.  The transport is
      * responsible for making sure the device does not automatically reconnect to the same network
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index d0e0cbe..3bd12c0 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -48,6 +48,7 @@
             mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
             mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
             mNetworkSpecifier = nc.mNetworkSpecifier;
+            mSignalStrength = nc.mSignalStrength;
         }
     }
 
@@ -60,6 +61,7 @@
         mNetworkCapabilities = mTransportTypes = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0;
         mNetworkSpecifier = null;
+        mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
     }
 
     /**
@@ -184,6 +186,28 @@
     private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_CAPTIVE_PORTAL;
 
     /**
+     * Network capabilities that are expected to be mutable, i.e., can change while a particular
+     * network is connected.
+     */
+    private static final long MUTABLE_CAPABILITIES =
+            // TRUSTED can change when user explicitly connects to an untrusted network in Settings.
+            // http://b/18206275
+            (1 << NET_CAPABILITY_TRUSTED) |
+            (1 << NET_CAPABILITY_VALIDATED) |
+            (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
+
+    /**
+     * Network capabilities that are not allowed in NetworkRequests. This exists because the
+     * NetworkFactory / NetworkAgent model does not deal well with the situation where a
+     * capability's presence cannot be known in advance. If such a capability is requested, then we
+     * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
+     * get immediately torn down because they do not have the requested capability.
+     */
+    private static final long NON_REQUESTABLE_CAPABILITIES =
+            (1 << NET_CAPABILITY_VALIDATED) |
+            (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
+
+    /**
      * Capabilities that are set by default when the object is constructed.
      */
     private static final long DEFAULT_CAPABILITIES =
@@ -278,8 +302,31 @@
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
     }
 
-    private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
-        return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
+    /**
+     * Convenience function that returns a human-readable description of the first mutable
+     * capability we find. Used to present an error message to apps that request mutable
+     * capabilities.
+     *
+     * @hide
+     */
+    public String describeFirstNonRequestableCapability() {
+        if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED";
+        if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL";
+        // This cannot happen unless the preceding checks are incomplete.
+        if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) {
+            return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities);
+        }
+        if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
+        if (hasSignalStrength()) return "signalStrength";
+        return null;
+    }
+
+    private boolean satisfiedByNetCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
+        long networkCapabilities = this.mNetworkCapabilities;
+        if (onlyImmutable) {
+            networkCapabilities = networkCapabilities & ~MUTABLE_CAPABILITIES;
+        }
+        return ((nc.mNetworkCapabilities & networkCapabilities) == networkCapabilities);
     }
 
     /** @hide */
@@ -287,6 +334,11 @@
         return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
     }
 
+    private boolean equalsNetCapabilitiesImmutable(NetworkCapabilities that) {
+        return ((this.mNetworkCapabilities & ~MUTABLE_CAPABILITIES) ==
+                (that.mNetworkCapabilities & ~MUTABLE_CAPABILITIES));
+    }
+
     /**
      * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if all the capabilities it provides are
      * typically provided by restricted networks.
@@ -555,26 +607,130 @@
     }
 
     /**
+     * Magic value that indicates no signal strength provided. A request specifying this value is
+     * always satisfied.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_UNSPECIFIED = Integer.MIN_VALUE;
+
+    /**
+     * Signal strength. This is a signed integer, and higher values indicate better signal.
+     * The exact units are bearer-dependent. For example, Wi-Fi uses RSSI.
+     */
+    private int mSignalStrength;
+
+    /**
+     * Sets the signal strength. This is a signed integer, with higher values indicating a stronger
+     * signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same RSSI units
+     * reported by WifiManager.
+     * <p>
+     * Note that when used to register a network callback, this specifies the minimum acceptable
+     * signal strength. When received as the state of an existing network it specifies the current
+     * value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means no value when received and has no
+     * effect when requesting a callback.
+     *
+     * @param signalStrength the bearer-specific signal strength.
+     * @hide
+     */
+    public void setSignalStrength(int signalStrength) {
+        mSignalStrength = signalStrength;
+    }
+
+    /**
+     * Returns {@code true} if this object specifies a signal strength.
+     *
+     * @hide
+     */
+    public boolean hasSignalStrength() {
+        return mSignalStrength > SIGNAL_STRENGTH_UNSPECIFIED;
+    }
+
+    /**
+     * Retrieves the signal strength.
+     *
+     * @return The bearer-specific signal strength.
+     * @hide
+     */
+    public int getSignalStrength() {
+        return mSignalStrength;
+    }
+
+    private void combineSignalStrength(NetworkCapabilities nc) {
+        this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength);
+    }
+
+    private boolean satisfiedBySignalStrength(NetworkCapabilities nc) {
+        return this.mSignalStrength <= nc.mSignalStrength;
+    }
+
+    private boolean equalsSignalStrength(NetworkCapabilities nc) {
+        return this.mSignalStrength == nc.mSignalStrength;
+    }
+
+    /**
      * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
-     * {@hide}
+     * @hide
      */
     public void combineCapabilities(NetworkCapabilities nc) {
         combineNetCapabilities(nc);
         combineTransportTypes(nc);
         combineLinkBandwidths(nc);
         combineSpecifiers(nc);
+        combineSignalStrength(nc);
     }
 
     /**
-     * Check if our requirements are satisfied by the given Capabilities.
-     * {@hide}
+     * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
+     *
+     * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+     * @param onlyImmutable if {@code true}, do not consider mutable requirements such as link
+     *         bandwidth, signal strength, or validation / captive portal status.
+     *
+     * @hide
+     */
+    private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
+        return (nc != null &&
+                satisfiedByNetCapabilities(nc, onlyImmutable) &&
+                satisfiedByTransportTypes(nc) &&
+                (onlyImmutable || satisfiedByLinkBandwidths(nc)) &&
+                satisfiedBySpecifier(nc) &&
+                (onlyImmutable || satisfiedBySignalStrength(nc)));
+    }
+
+    /**
+     * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
+     *
+     * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+     *
+     * @hide
      */
     public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
-        return (nc != null &&
-                satisfiedByNetCapabilities(nc) &&
-                satisfiedByTransportTypes(nc) &&
-                satisfiedByLinkBandwidths(nc) &&
-                satisfiedBySpecifier(nc));
+        return satisfiedByNetworkCapabilities(nc, false);
+    }
+
+    /**
+     * Check if our immutable requirements are satisfied by the given {@code NetworkCapabilities}.
+     *
+     * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+     *
+     * @hide
+     */
+    public boolean satisfiedByImmutableNetworkCapabilities(NetworkCapabilities nc) {
+        return satisfiedByNetworkCapabilities(nc, true);
+    }
+
+    /**
+     * Checks that our immutable capabilities are the same as those of the given
+     * {@code NetworkCapabilities}.
+     *
+     * @hide
+     */
+    public boolean equalImmutableCapabilities(NetworkCapabilities nc) {
+        if (nc == null) return false;
+        return (equalsNetCapabilitiesImmutable(nc) &&
+                equalsTransportTypes(nc) &&
+                equalsSpecifier(nc));
     }
 
     @Override
@@ -584,6 +740,7 @@
         return (equalsNetCapabilities(that) &&
                 equalsTransportTypes(that) &&
                 equalsLinkBandwidths(that) &&
+                equalsSignalStrength(that) &&
                 equalsSpecifier(that));
     }
 
@@ -595,7 +752,8 @@
                 ((int)(mTransportTypes >> 32) * 7) +
                 (mLinkUpBandwidthKbps * 11) +
                 (mLinkDownBandwidthKbps * 13) +
-                (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17));
+                (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17) +
+                (mSignalStrength * 19));
     }
 
     @Override
@@ -609,7 +767,9 @@
         dest.writeInt(mLinkUpBandwidthKbps);
         dest.writeInt(mLinkDownBandwidthKbps);
         dest.writeString(mNetworkSpecifier);
+        dest.writeInt(mSignalStrength);
     }
+
     public static final Creator<NetworkCapabilities> CREATOR =
         new Creator<NetworkCapabilities>() {
             @Override
@@ -621,6 +781,7 @@
                 netCap.mLinkUpBandwidthKbps = in.readInt();
                 netCap.mLinkDownBandwidthKbps = in.readInt();
                 netCap.mNetworkSpecifier = in.readString();
+                netCap.mSignalStrength = in.readInt();
                 return netCap;
             }
             @Override
@@ -678,6 +839,8 @@
         String specifier = (mNetworkSpecifier == null ?
                 "" : " Specifier: <" + mNetworkSpecifier + ">");
 
-        return "[" + transports + capabilities + upBand + dnBand + specifier + "]";
+        String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : "");
+
+        return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]";
     }
 }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 3f40484..02dae8a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.text.format.Time.MONTH_DAY;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -80,6 +81,54 @@
      */
     public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
 
+    /**
+     * Broadcast intent action for informing a custom component about a network policy
+     * notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_SHOW_NETWORK_POLICY_NOTIFICATION =
+            "android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION";
+
+    /**
+     * The sequence number associated with the notification - a higher number
+     * indicates previous notifications may be disregarded.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_SEQUENCE_NUMBER =
+            "android.net.extra.NOTIFICATION_SEQUENCE_NUMBER";
+
+    /**
+     * The type of notification that should be presented to the user.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_TYPE = "android.net.extra.NOTIFICATION_TYPE";
+
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_NONE = 0;
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_USAGE_WARNING = 1;
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_USAGE_REACHED_LIMIT = 2;
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT = 3;
+
+    /**
+     * The number of bytes used on the network in the notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_BYTES_USED = "android.net.extra.BYTES_USED";
+
+    /**
+     * The network policy for the network in the notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NETWORK_POLICY = "android.net.extra.NETWORK_POLICY";
+
     private final Context mContext;
     private INetworkPolicyManager mService;
 
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index e184ec4..7da4818 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -191,6 +191,24 @@
             mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
             return this;
         }
+
+        /**
+         * Sets the signal strength. This is a signed integer, with higher values indicating a
+         * stronger signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same
+         * RSSI units reported by WifiManager.
+         * <p>
+         * Note that when used to register a network callback, this specifies the minimum acceptable
+         * signal strength. When received as the state of an existing network it specifies the
+         * current value. A value of {@code SIGNAL_STRENGTH_UNSPECIFIED} means no value when
+         * received and has no effect when requesting a callback.
+         *
+         * @param signalStrength the bearer-specific signal strength.
+         * @hide
+         */
+        public Builder setSignalStrength(int signalStrength) {
+            mNetworkCapabilities.setSignalStrength(signalStrength);
+            return this;
+        }
     }
 
     // implement the Parcelable interface
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index cccc4be..1f3e9a7 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -101,6 +101,13 @@
      */
     public static final String EXTRA_INVALID_CHARGER = "invalid_charger";
 
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value set to the maximum charging current supported by the charger in micro amperes.
+     * {@hide}
+     */
+    public static final String EXTRA_MAX_CHARGING_CURRENT = "max_charging_current";
+
     // values for "status" field in the ACTION_BATTERY_CHANGED Intent
     public static final int BATTERY_STATUS_UNKNOWN = 1;
     public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java
index 8f5cf8b..29e868c 100644
--- a/core/java/android/os/BatteryProperties.java
+++ b/core/java/android/os/BatteryProperties.java
@@ -22,6 +22,7 @@
     public boolean chargerAcOnline;
     public boolean chargerUsbOnline;
     public boolean chargerWirelessOnline;
+    public int maxChargingCurrent;
     public int batteryStatus;
     public int batteryHealth;
     public boolean batteryPresent;
@@ -37,6 +38,7 @@
         chargerAcOnline = other.chargerAcOnline;
         chargerUsbOnline = other.chargerUsbOnline;
         chargerWirelessOnline = other.chargerWirelessOnline;
+        maxChargingCurrent = other.maxChargingCurrent;
         batteryStatus = other.batteryStatus;
         batteryHealth = other.batteryHealth;
         batteryPresent = other.batteryPresent;
@@ -55,6 +57,7 @@
         chargerAcOnline = p.readInt() == 1 ? true : false;
         chargerUsbOnline = p.readInt() == 1 ? true : false;
         chargerWirelessOnline = p.readInt() == 1 ? true : false;
+        maxChargingCurrent = p.readInt();
         batteryStatus = p.readInt();
         batteryHealth = p.readInt();
         batteryPresent = p.readInt() == 1 ? true : false;
@@ -68,6 +71,7 @@
         p.writeInt(chargerAcOnline ? 1 : 0);
         p.writeInt(chargerUsbOnline ? 1 : 0);
         p.writeInt(chargerWirelessOnline ? 1 : 0);
+        p.writeInt(maxChargingCurrent);
         p.writeInt(batteryStatus);
         p.writeInt(batteryHealth);
         p.writeInt(batteryPresent ? 1 : 0);
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index fce09dd..9f71ce1 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1180,6 +1180,37 @@
                     _data.recycle();
                 }
             }
+
+            @Override
+            public void createNewUserDir(int userHandle, String path) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userHandle);
+                    _data.writeString(path);
+                    mRemote.transact(Stub.TRANSACTION_createNewUserDir, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
+            public void deleteUserKey(int userHandle) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userHandle);
+                    mRemote.transact(Stub.TRANSACTION_deleteUserKey, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -1295,6 +1326,9 @@
         static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
         static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
 
+        static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 62;
+        static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -1850,6 +1884,21 @@
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_createNewUserDir: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userHandle = data.readInt();
+                    String path = data.readString();
+                    createNewUserDir(userHandle, path);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_deleteUserKey: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userHandle = data.readInt();
+                    deleteUserKey(userHandle);
+                    reply.writeNoException();
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -2159,4 +2208,19 @@
     public String getPrimaryStorageUuid() throws RemoteException;
     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
             throws RemoteException;
+
+    /**
+     * Creates the user data directory, possibly encrypted
+     * @param userHandle Handle of the user whose directory we are creating
+     * @param path Path at which to create the directory.
+     */
+    public void createNewUserDir(int userHandle, String path)
+        throws RemoteException;
+
+    /**
+     * Securely delete the user's encryption key
+     * @param userHandle Handle of the user whose key we are deleting
+     */
+    public void deleteUserKey(int userHandle)
+        throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d1f3743..1d92453 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -960,6 +960,24 @@
     }
 
     /** {@hide} */
+    public void createNewUserDir(int userHandle, File path) {
+        try {
+            mMountService.createNewUserDir(userHandle, path.getAbsolutePath());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public void deleteUserKey(int userHandle) {
+        try {
+            mMountService.deleteUserKey(userHandle);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
     public static File maybeTranslateEmulatedPathToInternal(File path) {
         final IMountService mountService = IMountService.Stub.asInterface(
                 ServiceManager.getService("mount"));
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fe95864..89de101 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5712,6 +5712,13 @@
         public static final String ASSISTANT = "assistant";
 
         /**
+         * Whether the camera launch gesture should be disabled.
+         *
+         * @hide
+         */
+        public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -5767,6 +5774,7 @@
             MOUNT_UMS_NOTIFY_ENABLED,
             SLEEP_TIMEOUT,
             DOUBLE_TAP_TO_WAKE,
+            CAMERA_GESTURE_DISABLED,
         };
 
         /**
@@ -8080,6 +8088,13 @@
          * @hide
          */
         public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
+
+        /**
+         * Whether to enable cellular on boot.
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot";
     }
 
     /**
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index a9bf92b..52852e7 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -761,6 +761,19 @@
      * Backs out one level of a navigation hierarchy or collapses the item that currently has
      * focus. */
     public static final int KEYCODE_NAVIGATE_OUT    = 263;
+    /** Key code constant: Primary stem key for Wear
+     * Main power/reset button on watch.
+     * @hide */
+    public static final int KEYCODE_STEM_PRIMARY = 264;
+    /** Key code constant: Generic stem key 1 for Wear
+     * @hide */
+    public static final int KEYCODE_STEM_1 = 265;
+    /** Key code constant: Generic stem key 2 for Wear
+     * @hide */
+    public static final int KEYCODE_STEM_2 = 266;
+    /** Key code constant: Generic stem key 3 for Wear
+     * @hide */
+    public static final int KEYCODE_STEM_3 = 267;
     /** Key code constant: Skip forward media key. */
     public static final int KEYCODE_MEDIA_SKIP_FORWARD = 272;
     /** Key code constant: Skip backward media key. */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ac807e..b3e3ecb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7391,11 +7391,7 @@
      * @hide
      */
     public void getOutsets(Rect outOutsetRect) {
-        if (mAttachInfo != null) {
-            outOutsetRect.set(mAttachInfo.mOutsets);
-        } else {
-            outOutsetRect.setEmpty();
-        }
+        outOutsetRect.set(mAttachInfo.mOutsets);
     }
 
     /**
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6962711..0cc1b25 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -612,7 +612,7 @@
         View listItem = view;
         try {
             View v;
-            while (!(v = (View) listItem.getParent()).equals(this)) {
+            while ((v = (View) listItem.getParent()) != null && !v.equals(this)) {
                 listItem = v;
             }
         } catch (ClassCastException e) {
@@ -620,11 +620,13 @@
             return INVALID_POSITION;
         }
 
-        // Search the children for the list item
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            if (getChildAt(i).equals(listItem)) {
-                return mFirstPosition + i;
+        if (listItem != null) {
+            // Search the children for the list item
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                if (getChildAt(i).equals(listItem)) {
+                    return mFirstPosition + i;
+                }
             }
         }
 
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 55493c3..6da0f63 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -29,6 +29,21 @@
     // Temporary constants go here, to await migration to MetricsConstants.
     // next value is 239;
     public static final int ACTION_ASSIST_LONG_PRESS = 239;
+    public static final int FINGERPRINT_ENROLLING = 240;
+    public static final int FINGERPRINT_FIND_SENSOR = 241;
+    public static final int FINGERPRINT_ENROLL_FINISH = 242;
+    public static final int FINGERPRINT_ENROLL_INTRO = 243;
+    public static final int FINGERPRINT_ENROLL_ONBOARD = 244;
+    public static final int FINGERPRINT_ENROLL_SIDECAR = 245;
+    public static final int FINGERPRINT_ENROLLING_SETUP = 246;
+    public static final int FINGERPRINT_FIND_SENSOR_SETUP = 247;
+    public static final int FINGERPRINT_ENROLL_FINISH_SETUP = 248;
+    public static final int FINGERPRINT_ENROLL_INTRO_SETUP = 249;
+    public static final int FINGERPRINT_ENROLL_ONBOARD_SETUP = 250;
+    public static final int ACTION_FINGERPRINT_ENROLL = 251;
+    public static final int ACTION_FINGERPRINT_AUTH = 252;
+    public static final int ACTION_FINGERPRINT_DELETE = 253;
+    public static final int ACTION_FINGERPRINT_RENAME = 254;
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c1645c3..feed3903 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -69,5 +69,10 @@
 
     void showAssistDisclosure();
     void startAssist(in Bundle args);
+
+    /**
+     * Notifies the status bar that a camera launch gesture has been detected.
+     */
+    void onCameraLaunchGestureDetected();
 }
 
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index dfb7c50..4e4552d 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import android.app.trust.IStrongAuthTracker;
 import com.android.internal.widget.VerifyCredentialResponse;
 
 /** {@hide} */
@@ -35,4 +36,7 @@
     boolean checkVoldPassword(int userId);
     boolean havePattern(int userId);
     boolean havePassword(int userId);
+    void registerStrongAuthTracker(in IStrongAuthTracker tracker);
+    void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
+    void requireStrongAuth(int strongAuthReason, int userId);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 86d11be..d24b10b 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,18 +16,19 @@
 
 package com.android.internal.widget;
 
-import android.Manifest;
+import android.annotation.IntDef;
 import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
+import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
-import android.bluetooth.BluetoothClass;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.AsyncTask;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -38,9 +39,12 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import com.google.android.collect.Lists;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -228,7 +232,7 @@
     public void reportFailedPasswordAttempt(int userId) {
         getDevicePolicyManager().reportFailedPasswordAttempt(userId);
         getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
-        getTrustManager().reportRequireCredentialEntry(userId);
+        requireCredentialEntry(userId);
     }
 
     public void reportSuccessfulPasswordAttempt(int userId) {
@@ -1163,10 +1167,32 @@
     }
 
     /**
-     * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int)
+     * Disable trust until credentials have been entered for user {@param userId}.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     *
+     * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
      */
     public void requireCredentialEntry(int userId) {
-        getTrustManager().reportRequireCredentialEntry(userId);
+        requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+    }
+
+    /**
+     * Requests strong authentication for user {@param userId}.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     *
+     * @param strongAuthReason a combination of {@link StrongAuthTracker.StrongAuthFlags} indicating
+     *                         the reason for and the strength of the requested authentication.
+     * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
+     */
+    public void requireStrongAuth(@StrongAuthTracker.StrongAuthFlags int strongAuthReason,
+            int userId) {
+        try {
+            getLockSettings().requireStrongAuth(strongAuthReason, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error while requesting strong auth: " + e);
+        }
     }
 
     private void onAfterChangingPassword(int userHandle) {
@@ -1198,4 +1224,154 @@
     private boolean shouldEncryptWithCredentials(boolean defaultValue) {
         return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet();
     }
+
+
+    public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
+        try {
+            getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not register StrongAuthTracker");
+        }
+    }
+
+    public void unregisterStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
+        try {
+            getLockSettings().unregisterStrongAuthTracker(strongAuthTracker.mStub);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not unregister StrongAuthTracker", e);
+        }
+    }
+
+    /**
+     * Tracks the global strong authentication state.
+     */
+    public static class StrongAuthTracker {
+
+        @IntDef(flag = true,
+                value = { STRONG_AUTH_NOT_REQUIRED,
+                        STRONG_AUTH_REQUIRED_AFTER_BOOT,
+                        STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
+                        SOME_AUTH_REQUIRED_AFTER_USER_REQUEST})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface StrongAuthFlags {}
+
+        /**
+         * Strong authentication is not required.
+         */
+        public static final int STRONG_AUTH_NOT_REQUIRED = 0x0;
+
+        /**
+         * Strong authentication is required because the user has not authenticated since boot.
+         */
+        public static final int STRONG_AUTH_REQUIRED_AFTER_BOOT = 0x1;
+
+        /**
+         * Strong authentication is required because a device admin has requested it.
+         */
+        public static final int STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW = 0x2;
+
+        /**
+         * Some authentication is required because the user has temporarily disabled trust.
+         */
+        public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4;
+
+        /**
+         * Strong authentication is required because the user has been locked out after too many
+         * attempts.
+         */
+        public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
+
+        public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT;
+        private static final int ALLOWING_FINGERPRINT = SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
+
+        final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
+
+        private final H mHandler;
+
+        public StrongAuthTracker() {
+            this(Looper.myLooper());
+        }
+
+        /**
+         * @param looper the looper on whose thread calls to {@link #onStrongAuthRequiredChanged}
+         *               will be scheduled.
+         */
+        public StrongAuthTracker(Looper looper) {
+            mHandler = new H(looper);
+        }
+
+        /**
+         * Returns {@link #STRONG_AUTH_NOT_REQUIRED} if strong authentication is not required,
+         * otherwise returns a combination of {@link StrongAuthFlags} indicating why strong
+         * authentication is required.
+         *
+         * @param userId the user for whom the state is queried.
+         */
+        public @StrongAuthFlags int getStrongAuthForUser(int userId) {
+            return mStrongAuthRequiredForUser.get(userId, DEFAULT);
+        }
+
+        /**
+         * @return true if unlocking with trust alone is allowed for {@param userId} by the current
+         * strong authentication requirements.
+         */
+        public boolean isTrustAllowedForUser(int userId) {
+            return getStrongAuthForUser(userId) == STRONG_AUTH_NOT_REQUIRED;
+        }
+
+        /**
+         * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
+         * current strong authentication requirements.
+         */
+        public boolean isFingerprintAllowedForUser(int userId) {
+            return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
+        }
+
+        /**
+         * Called when the strong authentication requirements for {@param userId} changed.
+         */
+        public void onStrongAuthRequiredChanged(int userId) {
+        }
+
+        void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
+                int userId) {
+
+            int oldValue = getStrongAuthForUser(userId);
+            if (strongAuthFlags != oldValue) {
+                if (strongAuthFlags == DEFAULT) {
+                    mStrongAuthRequiredForUser.delete(userId);
+                } else {
+                    mStrongAuthRequiredForUser.put(userId, strongAuthFlags);
+                }
+                onStrongAuthRequiredChanged(userId);
+            }
+        }
+
+
+        final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() {
+            @Override
+            public void onStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
+                    int userId) {
+                mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED,
+                        strongAuthFlags, userId).sendToTarget();
+            }
+        };
+
+        private class H extends Handler {
+            static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1;
+
+            public H(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED:
+                        handleStrongAuthRequiredChanged(msg.arg1, msg.arg2);
+                        break;
+                }
+            }
+        };
+    }
 }
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 35ed63b..d88f479 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -89,14 +89,21 @@
                 }
             };
     private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+        private Runnable mRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mDismissed) {
+                    dismiss();
+                } else {
+                    cancel();
+                }
+                resetMembers();
+            }
+        };
+
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (mDismissed) {
-                dismiss();
-            } else {
-                cancel();
-            }
-            resetMembers();
+            post(mRunnable);
         }
     };
     private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 6b07a47..fc15b964 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -20,6 +20,10 @@
     LOCAL_CFLAGS += -DENABLE_CPUSETS
 endif
 
+ifneq ($(ENABLE_SCHED_BOOST),)
+    LOCAL_CFLAGS += -DENABLE_SCHED_BOOST
+endif
+
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
 LOCAL_CFLAGS += -DU_USING_ICU_NAMESPACE=0
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index ec56507..7d0afdc 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -138,7 +138,7 @@
 (JNIEnv *env, jclass clazz, jstring opPackageName)
 {
     ScopedUtfChars opPackageNameUtf(env, opPackageName);
-    return (jlong) new SensorManager(String16(opPackageNameUtf.c_str()));
+    return (jlong) &SensorManager::getInstanceForPackage(String16(opPackageNameUtf.c_str()));
 }
 
 static jboolean
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index ba08237..e10a644 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <algorithm>
 #include <memory>
+#include <vector>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -1659,8 +1660,7 @@
             lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
         }
 
-        camera_metadata_entry entry2 =
-                results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+        camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
 
         camera_metadata_entry entry =
                 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
@@ -1685,6 +1685,45 @@
             }
         }
 
+
+        // Set up bad pixel correction list
+        camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
+
+        if ((entry3.count % 2) != 0) {
+            ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
+                    __FUNCTION__);
+            jniThrowRuntimeException(env, "failed to add hotpixel map.");
+            return nullptr;
+        }
+
+        // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
+        std::vector<uint32_t> v;
+        for (size_t i = 0; i < entry3.count; i+=2) {
+            int32_t x = entry3.data.i32[i];
+            int32_t y = entry3.data.i32[i + 1];
+            x -= static_cast<int32_t>(xmin);
+            y -= static_cast<int32_t>(ymin);
+            if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
+                    static_cast<uint32_t>(y) >= width) {
+                continue;
+            }
+            v.push_back(x);
+            v.push_back(y);
+        }
+        const uint32_t* badPixels = &v[0];
+        uint32_t badPixelCount = v.size();
+
+        if (badPixelCount > 0) {
+            err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
+
+            if (err != OK) {
+                ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
+                jniThrowRuntimeException(env, "failed to add hotpixel map.");
+                return nullptr;
+            }
+        }
+
+
         size_t listSize = builder.getSize();
         uint8_t opcodeListBuf[listSize];
         err = builder.buildOpList(opcodeListBuf);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5e7b444..414cbb4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -408,6 +408,27 @@
   }
 }
 
+#ifdef ENABLE_SCHED_BOOST
+static void SetForkLoad(bool boost) {
+  // set scheduler knob to boost forked processes
+  pid_t currentPid = getpid();
+  // fits at most "/proc/XXXXXXX/sched_init_task_load\0"
+  char schedPath[35];
+  snprintf(schedPath, sizeof(schedPath), "/proc/%u/sched_init_task_load", currentPid);
+  int schedBoostFile = open(schedPath, O_WRONLY);
+  if (schedBoostFile < 0) {
+    ALOGW("Unable to set zygote scheduler boost");
+    return;
+  }
+  if (boost) {
+    write(schedBoostFile, "100\0", 4);
+  } else {
+    write(schedBoostFile, "0\0", 2);
+  }
+  close(schedBoostFile);
+}
+#endif
+
 // Utility routine to fork zygote and specialize the child process.
 static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                      jint debug_flags, jobjectArray javaRlimits,
@@ -418,6 +439,10 @@
                                      jstring instructionSet, jstring dataDir) {
   SetSigChldHandler();
 
+#ifdef ENABLE_SCHED_BOOST
+  SetForkLoad(true);
+#endif
+
   pid_t pid = fork();
 
   if (pid == 0) {
@@ -558,6 +583,12 @@
     }
   } else if (pid > 0) {
     // the parent process
+
+#ifdef ENABLE_SCHED_BOOST
+    // unset scheduler knob
+    SetForkLoad(false);
+#endif
+
   }
   return pid;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 99e55bf..86659ae 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -320,6 +320,8 @@
     <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
     <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+
+    <protected-broadcast android:name="android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION" />
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -865,6 +867,11 @@
     <permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide Allows an application to modify cell broadcasts through the content provider.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MODIFY_CELL_BROADCASTS"
+                android:protectionLevel="signature|system" />
+
     <!-- =============================================================== -->
     <!-- Permissions for setting the device alarm                        -->
     <!-- =============================================================== -->
@@ -2505,6 +2512,10 @@
     <permission android:name="android.permission.MANAGE_FINGERPRINT"
         android:protectionLevel="system|signature" />
 
+    <!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
+    <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to control keyguard.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.CONTROL_KEYGUARD"
diff --git a/core/res/res/values-mcc219-mnc02/config.xml b/core/res/res/values-mcc219-mnc02/config.xml
new file mode 100644
index 0000000..2ac6ba6
--- /dev/null
+++ b/core/res/res/values-mcc219-mnc02/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You my obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21901</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-watch/strings.xml b/core/res/res/values-watch/strings.xml
index 4ea2b52..e5991fc 100644
--- a/core/res/res/values-watch/strings.xml
+++ b/core/res/res/values-watch/strings.xml
@@ -23,4 +23,50 @@
     <string name="android_upgrading_apk">App
         <xliff:g id="number" example="123">%1$d</xliff:g> of
         <xliff:g id="number" example="123">%2$d</xliff:g>.</string>
+
+    <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. Override from base which says "Body Sensors". [CHAR_LIMIT=25] -->
+    <string name="permgrouplab_sensors">Sensors</string>
+
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_contactswear">access your contacts</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_locationwear">access this watch\'s location</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_calendarwear">access your calendar</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_smswear">send and view SMS messages</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_storagewear">access photos, media, and files on your watch</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_microphonewear">record audio</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_camerawear">take pictures and record video</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_phonewear">make and manage phone calls</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permgrouplab_sensorswear">access sensor data about your vital signs</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_statusBarServicewear">be the status bar</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_bodySensorswear">access body sensors (like heart rate monitors)</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_accessFineLocationwear">access precise location (GPS and network-based)</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_accessCoarseLocationwear">access approximate location (network-based)</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_sim_communicationwear">send commands to the SIM</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_createNetworkSocketswear">have full network access</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_manageProfileAndDeviceOwnerswear">manage profile and device owners</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_changeWimaxStatewear">change WiMAX state</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_handoverStatuswear">receive Android Beam transfer status</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_route_media_outputwear">route media output</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_readInstallSessionswear">read install sessions</string>
+    <!-- Description of a  permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+    <string name="permlab_requestInstallPackageswear">request install packages</string>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 67abe8d..a0246560 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1806,6 +1806,10 @@
         <enum name="KEYCODE_NAVIGATE_NEXT" value="261" />
         <enum name="KEYCODE_NAVIGATE_IN" value="262" />
         <enum name="KEYCODE_NAVIGATE_OUT" value="263" />
+        <enum name="KEYCODE_STEM_PRIMARY" value="264" />
+        <enum name="KEYCODE_STEM_1" value="265" />
+        <enum name="KEYCODE_STEM_2" value="266" />
+        <enum name="KEYCODE_STEM_3" value="267" />
         <enum name="KEYCODE_MEDIA_SKIP_FORWARD" value="272" />
         <enum name="KEYCODE_MEDIA_SKIP_BACKWARD" value="273" />
         <enum name="KEYCODE_MEDIA_STEP_FORWARD" value="274" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ae8cae8..694d263 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -619,6 +619,17 @@
         <!-- rotation: 270 (rotate CW)  --> <item>-25</item> <item>65</item>
     </integer-array>
 
+    <!-- Indicate the name of the window orientation sensor type if present. A
+         window orientation sensor produces values to be used in lieu of the
+         typical, accelerometer based sensor. It must only produce integral
+         values between 0 and 3, inclusive, with each one corresponding to a
+         given rotation:
+            0: 0 degrees of rotation (natural)
+            1: 90 degrees of rotation (rotate CCW)
+            2: 180 degrees of rotation (reverse)
+            3: 270 degrees of rotation (rotate CW) -->
+    <string name="config_orientationSensorType" translatable="false">@null</string>
+
     <!-- Lid switch behavior -->
 
     <!-- The number of degrees to rotate the display when the keyboard is open.
@@ -2270,4 +2281,13 @@
     <string-array name="config_cell_retries_per_error_code">
     </string-array>
 
+    <!-- The OEM specified sensor type for the gesture to launch the camear app. -->
+    <integer name="config_cameraLaunchGestureSensorType">-1</integer>
+    <!-- The OEM specified sensor string type for the gesture to launch camera app, this value
+         must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
+    <string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
+
+    <!-- Name of the component to handle network policy notifications. If present,
+         disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
+    <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 262aa76..4e94a64 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1547,6 +1547,7 @@
   <java-symbol type="string" name="bugreport_title" />
   <java-symbol type="string" name="bugreport_message" />
   <java-symbol type="string" name="bugreport_status" />
+  <java-symbol type="string" name="config_orientationSensorType" />
   <java-symbol type="string" name="faceunlock_multiple_failures" />
   <java-symbol type="string" name="global_action_power_off" />
   <java-symbol type="string" name="global_actions_airplane_mode_off_status" />
@@ -2318,6 +2319,10 @@
   <java-symbol type="array" name="config_cell_retries_per_error_code" />
   <java-symbol type="drawable" name="ic_more_items" />
 
-  <java-symbol type="drawable" name="platlogo_m" />
+  <!-- Gesture -->
+  <java-symbol type="integer" name="config_cameraLaunchGestureSensorType" />
+  <java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
 
+  <java-symbol type="string" name="config_networkPolicyNotificationComponent" />
+  <java-symbol type="drawable" name="platlogo_m" />
 </resources>
diff --git a/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png b/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png
new file mode 100644
index 0000000..99e60e9
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-launch.png b/docs/html/images/training/auto-desktop-head-unit-launch.png
new file mode 100644
index 0000000..012857a4
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-launch.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-server-running.png b/docs/html/images/training/auto-desktop-head-unit-server-running.png
new file mode 100644
index 0000000..5aa3b83
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-server-running.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png b/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png
new file mode 100644
index 0000000..4d48ac4
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png
Binary files differ
diff --git a/docs/html/tools/help/desktop-head-unit.jd b/docs/html/tools/help/desktop-head-unit.jd
new file mode 100644
index 0000000..981979c
--- /dev/null
+++ b/docs/html/tools/help/desktop-head-unit.jd
@@ -0,0 +1,439 @@
+page.title=Desktop Head Unit
+page.tags="auto", "car", "testing","dhu"
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+    <h2>In this document</h2>
+    <ol>
+      <li><a href="#launching-dhu">Launching the DHU</a></li>
+      <li><a href="#dhu-commands">Issuing DHU Commands</a></li>
+    </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}training/auto/start/index.html#test-it-dhu">Run and Test Auto Apps</a></li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>The Desktop Head Unit (DHU) enables your development machine to emulate an Android Auto
+head unit, so you can easily run and test Android Auto apps. The DHU runs on
+Windows, Mac, and Linux hosts and replaces previous Android Auto simulators,
+such as the Android Media Browser and Messaging
+simulators.</p>
+
+<p class="note"><strong>Note:</strong> For other information about testing Auto apps, see the
+training lesson
+<a href="{@docRoot}training/auto/start/index.html#test-it-dhu">Run and Test Auto Apps</a>.  </p>
+
+
+<h2 id="launching-dhu">Launching the DHU</h2>
+
+<p>
+  To launch the DHU, run the <code>desktop-head-unit.exe</code> (on Windows)
+  or <code>desktop-head-unit</code> (on Mac or Linux) command, as described in
+  <a href="{@docRoot}training/auto/start/index.html#connecting-dhu">Connecting
+  the DHU to your mobile device</a>.
+</p>
+
+<p>
+  By default, the DHU emulates the most common form of Android Auto-compatible
+  head unit, which uses a touch screen user interface. You can simulate user
+  touches by clicking the DHU with a mouse. To emulate head units which use
+  a rotary controller for input, you can use the <code>-i controller</code> flag,
+  as in this example:
+</p>
+
+<pre class="no-pretty-print">$ ./desktop-head-unit -i controller</pre>
+
+<p>
+  When the DHU is in rotary-controller mode you can simulate controller
+  operations by using keyboard shortcuts, as described in <a href=
+  "#cmd-bindings">DHU commands and key bindings</a>. If the DHU is in rotary
+  controller mode, it ignores mouse clicks; you must operate Android Auto with
+  the simulated rotary controller operations.
+</p>
+
+<h2 id="dhu-commands">Issuing DHU Commands</h2>
+
+<p>
+  DHU commands allow you to test your app with Android Auto features, such as
+  playing voice input or switching between night and day display mode. You can issue commands to
+  the DHU by running commands from the terminal window where you launched DHU.
+  You can also issue commands by selecting the DHU window and
+  using keyboard shortcuts. The DHU commands
+  and key bindings for all controls are listed in <a href="#cmd-bindings">DHU
+  commands and key bindings</a>.
+</p>
+
+
+<h3 id="day-night">Switching between day and night mode</h3>
+
+<p>
+  Android Auto supports different color schemes for day and night. You should test your app in both
+  day and night mode. You can switch between night and day mode in either of the
+  following ways:
+</p>
+
+<ul>
+  <li>Run the command <code>daynight</code> in the terminal where you launched the DHU.
+  </li>
+
+  <li>Select the DHU window and press the <strong>N</strong> key.
+  </li>
+</ul>
+
+<h3>Microphone testing</h3>
+
+<p>The DHU supports using a microphone for voice input. You can also instruct the DHU to treat
+a pre-recorded voice track as input, as if the DHU had heard the track through the microphone.</p>
+
+<p>To use a pre-recorded sound file as input, enter this command: </p>
+<pre class="no-pretty-print">
+$ mic play &lt;sound_file_path&gt;/&lt;sound_file&gt;.wav
+</pre>
+
+<p>For your convenience, we have provided the following sound files for common
+voice commands. These sound files are installed in the
+<code>&lt;sdk&gt;/extras/google/auto/voice/</code> directory.</p>
+
+<dl>
+  <dt>
+    <code>exitnav.wav</code>
+  </dt>
+
+  <dd>
+    "Exit navigation."
+  </dd>
+
+  <dt>
+    <code>navgoogle.wav</code>
+  </dt>
+
+  <dd>
+    "Navigate to 1600 Amphitheatre Parkway, Mountain View."
+  </dd>
+
+  <dt>
+    <code>navsoh.wav</code>
+  </dt>
+
+  <dd>
+    "Navigate to Sydney Opera House."
+  </dd>
+
+  <dt>
+    <code>nextturn.wav</code>
+  </dt>
+
+  <dd>
+    "When is my next turn?"
+  </dd>
+
+  <dt>
+    <code>showalternateroute.wav</code>
+  </dt>
+
+  <dd>
+    "Show alternate routes.""
+  </dd>
+
+  <dt>
+    <code>howlong.wav</code>
+  </dt>
+
+  <dd>
+    "How long until I get there?"
+  </dd>
+
+  <dt>
+    <code>navhome.wav</code>
+  </dt>
+
+  <dd>
+    "Navigate to home."
+  </dd>
+
+  <dt>
+    <code>navwork.wav</code>
+  </dt>
+
+  <dd>
+    "Navigate to work.""
+  </dd>
+
+  <dt>
+    <code>pause.wav</code>
+  </dt>
+
+  <dd>
+    "Pause music."
+  </dd>
+
+  <dt>
+    <code>showtraffic.wav</code>
+  </dt>
+
+  <dd>
+    "Show traffic."
+  </dd>
+</dl>
+<h3 id="cmd-bindings">DHU commands and key bindings</h3>
+
+<p>The DHU supports the following commands.</p>
+
+<p class="table-caption" id="table-commands"><strong>Table 1.</strong> Commands and key bindings</p>
+<table>
+<tr>
+  <th>Category</th>
+  <th>Command</th>
+  <th>Subcommand</th>
+  <th>Argument(s)</th>
+  <th>Keyboard Shortcut(s)</th>
+  <th>Description</th>
+</tr>
+
+<!--system-->
+
+<tr>
+<td rowspan="4">System</td>
+<td>help</td>
+<td></td>
+<td>[command]</td>
+<td></td>
+<td>Shows the full command set. Specifying a command name (for example, <code>help day</code>)
+  causes the system to show help for that command.</td>
+</tr>
+
+<tr>
+
+<td>quit</td>
+<td></td>
+<td></td>
+<td>Alt+q</td>
+<td>Quits the head unit.</td>
+</tr>
+
+<tr>
+
+<td>sleep</td>
+<td></td>
+<td>[seconds]</td>
+<td></td>
+<td>Sleeps for one second. Specifying an argument (for example, <code>sleep 30</code>) causes the
+system to sleep the specified number of seconds. This command
+is useful if you are writing scripts for the DHU. (You can run a script by using I/O redirection
+from the command line: <code>./desktop-head-unit &lt; script.txt</code> loads commands from the
+file <code>script.txt</code>.)</td>
+</tr>
+
+<tr>
+
+<td>screenshot</td>
+<td></td>
+<td>filename.png</td>
+<td></td>
+<td>Saves a screenshot to <code>filename.png</code>.</td>
+</tr>
+
+
+<!--microphone-->
+
+<tr>
+<td rowspan="3">Microphone</td>
+<td rowspan="3">mic</td>
+<td>begin</td>
+<td></td>
+<td>m </td>
+<td>Activates the microphone (equivalent to clicking the steering wheel's microphone button) and
+waits for input from the computer microphone.</td>
+</tr>
+
+<tr>
+
+
+<td>play</td>
+<td>filename.wav</td>
+<td></td>
+<td>Causes the DHU to treat <code>filename.wav</code> as voice input, as if it had heard that sound
+  through the microphone. You do not hear the sound file being played, but you do hear
+  the response from Android Auto.</td>
+</tr>
+
+<tr>
+
+
+<td>repeat</td>
+<td></td>
+<td></td>
+<td>Repeats the last <code>mic play</code> command, as if you had called <code>mic play</code>
+  again with the same sound file parameter.</td>
+</tr>
+
+<!--Input-->
+
+<tr>
+<td rowspan="7">Input</td>
+<td rowspan="6">dpad</td>
+<td>up <br> down <br> left <br> right</td>
+<td></td>
+<td>Arrow keys</td>
+<td>Simulates moving the rotary controller.</td>
+</tr>
+
+<tr>
+
+
+<td>soft left <br> soft right</td>
+<td></td>
+<td>Shift+Arrow keys</td>
+<td>Simulates pressing the side buttons available on some rotary controllers.</td>
+</tr>
+
+<tr>
+
+
+<td>click</td>
+<td></td>
+<td>Return</td>
+<td>Simulates pressing the rotary controller.</td>
+</tr>
+
+<tr>
+
+
+<td>back</td>
+<td></td>
+<td>Backspace</td>
+<td>Simulates pressing the <strong>back</strong> button available below some rotary
+  controllers.</td>
+</tr>
+
+<tr>
+
+
+<td>rotate left <br> rotate right</td>
+<td></td>
+<td>1 <br> 2</td>
+<td>Simulates rotating the rotary controller left (counter-clockwise) or right (clockwise).</td>
+</tr>
+
+<tr>
+
+
+<td>flick left <br> flick right</td>
+<td></td>
+<td>Shift+1 <br> Shift+2</td>
+<td>Simulates a fast spin of the rotary controller to the left (counter-clockwise) or right
+  (clockwise).</td>
+</tr>
+
+<tr>
+
+<td>tap</td>
+<td></td>
+<td>x y</td>
+<td></td>
+<td>Simulates a touch event at the specified coordinates. For example, <code>tap 50 100</code></td>
+</tr>
+
+
+<!--Day/Night-->
+
+<tr>
+<td rowspan="3">Day/Night</td>
+<td>day</td>
+<td></td>
+<td></td>
+<td>Shift+n</td>
+<td>Activates day mode (high brightness, full color).</td>
+</tr>
+
+<tr>
+
+<td>night</td>
+<td></td>
+<td> </td>
+<td>Ctrl+n </td>
+<td>Activates night mode (low brightness, high contrast).</td>
+</tr>
+
+<tr>
+
+<td>daynight</td>
+<td></td>
+<td></td>
+<td>n </td>
+<td>Toggles current day/night mode.</td>
+</tr>
+
+</table>
+
+
+
+
+<h2 id="auto-simulators">Media Browser and Messaging Simulators</h2>
+
+<p class="caution"><strong>Important:</strong> Use of the Android Media Browser and Messaging
+Simulators for testing Android Auto apps is deprecated. Instead, we recommend using the
+Desktop Head Unit, which enables your development machine to act as if it were an Android Auto head
+unit.</p>
+
+<p>To get the simulators, open the
+<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and download
+them from <strong>Extras &gt; Android Auto API Simulators</strong>.</p>
+
+<p>Before you begin testing, compile your app in your development environment.
+Install your app and the Android simulator for the features you want to test
+(that is, audio or messaging) on a physical or virtual device running Android
+5.0 (API level 21) or higher. To check the version of Android on the device, go
+to <strong>Settings &gt; About phone</strong> (or <strong>About tablet</strong>)
+<strong>&gt; Android Version</strong>.</p>
+
+<h3 id="testing-audio-apps">Testing audio apps</h3>
+<p>To run and test audio apps:</p>
+
+<ol>
+<li>Install the Android Media Browser simulator
+({@code &lt;sdk&gt;/extras/google/simulators/media-browser-simulator.apk}) on
+the test device. You can do this using
+the <a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
+<li>Enable <a href="{@docRoot}tools/device.html#developer-device-options">
+developer options</a> on the test device.</li>
+<li>Install your app on the test device.</li>
+<li>Launch the Android Media Browser simulator to see how your audio app
+appears in Auto. If your app does not appear, stop the simulator from
+<strong>Settings &gt; Apps</strong> and restart it.</li>
+</ol>
+
+
+<h3 id="testing-messaging-apps">Testing messaging apps</h3>
+<p>To run and test messaging apps:</p>
+
+<ol>
+<li>Install the Android Messaging simulator
+  ({@code &lt;sdk&gt;/extras/google/simulators/messaging-simulator.apk})
+on the test device. You can do this using the
+<a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
+<li>Enable the simulator to read notifications posted on the system:
+<ol type="a">
+	<li>Enable <a href="{@docRoot}tools/device.html#developer-device-options">
+developer options</a> on the test device.</li>
+  <li>Click <strong>Settings &gt; Sounds &amp; Notifications &gt; Notification
+    Access</strong> and check the box labeled
+    <strong>Messaging Simulator</strong>.</li>
+</ol>
+<li>Install your app on the test device.</li>
+<li>Launch the Android Messaging Simulator to see how your messaging app appears
+in Auto. If your app does not appear, stop the simulator from
+<strong>Settings &gt; Apps</strong> and restart it.</li>
+</ol>
+
+
+
+
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd
index b3af7a2..19f93e9 100644
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/tools/support-library/features.jd
@@ -307,8 +307,8 @@
 on how to set up your project, follow the instructions in <a
 href="{@docRoot}tools/support-library/setup.html#libs-with-res">Adding libraries
 with resources</a>. If you are developing in Eclipse/ADT, make sure to include
-both the <code>android-support-v7-mediarouter.jar</code> and
-<code>android-support-v7-appcompat.jar</code> files.</p>
+the <code>android-support-v7-mediarouter.jar</code>, <code>android-support-v7-appcompat.jar</code>,
+and <code>android-support-v7-palette.jar</code> files.</p>
 
 <p>If you are using Android Studio, all you need to do is specify the Gradle build
 script dependency identifier <code>com.android.support:support-v7-mediarouter:&lt;revision&gt;</code>,
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index db19d4f..72f9f21 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -160,6 +160,7 @@
       <li><a href="<?cs var:toroot ?>tools/help/avd-manager.html">AVD Manager</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/bmgr.html">bmgr</a>
       <li><a href="<?cs var:toroot ?>tools/help/monitor.html">Device Monitor</a></li>
+      <li><a href="<?cs var:toroot ?>tools/help/desktop-head-unit.html">Desktop Head Unit</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/dmtracedump.html">dmtracedump</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/draw9patch.html">Draw 9-Patch</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/emulator.html">Emulator</a></li>
diff --git a/docs/html/training/auto/start/index.jd b/docs/html/training/auto/start/index.jd
index 22e7521..f6cdbd1 100644
--- a/docs/html/training/auto/start/index.jd
+++ b/docs/html/training/auto/start/index.jd
@@ -16,7 +16,7 @@
   <ol>
     <li><a href="#dev-project">Set Up an Auto Project</a></li>
     <li><a href="#build-it">Build Auto Apps</a></li>
-    <li><a href="#test-it">Run and Test Auto Apps</a></li>
+    <li><a href="#test-it-dhu">Run and Test Auto Apps </a></li>
   </ol>
 
  <h2>You should also read</h2>
@@ -149,57 +149,124 @@
 more information, see
 <a href="{@docRoot}distribute/essentials/quality/auto.html">Auto App Quality</a>.</p>
 
-<h2 id="test-it">Run and Test Auto Apps</h2>
 
-<p>As you prepare to publish your app, make sure that your app looks correct
-when projected on the Auto user interface. Use the Android Media Browser
-simulator and Android Messaging simulators to view and test your audio or
-messaging apps in a screen that looks similar to what is projected on Auto.</p>
 
-<p>To get the simulators, open the
-<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and download
-them from <strong>Extras &gt; Android Auto API Simulators</strong>.</p>
+<h2 id="test-it-dhu">Run and Test Auto Apps </h2>
 
-<p>Before you begin testing, compile your app in your development environment.
-Install your app and the Android simulator for the features you want to test
-(that is, audio or messaging) on a physical or virtual device running Android
-5.0 (API level 21) or higher. To check the version of Android on the device, go
-to <strong>Settings &gt; About &gt; Android Version</strong>.</p>
+<p>
+  As you develop, you can run and test your app on your development machine
+  using the <em>Desktop Head Unit</em> (DHU). The DHU replaces the existing
+  simulators and enables your development machine to simulate a vehicle
+  dashboard system running Android Auto.
+</p>
 
-<h3 id="testing-audio-apps">Testing audio apps</h3>
-<p>To run and test audio apps:</p>
+<h3 id="installing-dhu">Installing the DHU</h3>
 
 <ol>
-<li>Install the Android Media Browser simulator
-({@code &lt;sdk&gt;/extras/google/simulators/media-browser-simulator.apk}) on
-the test device. You can do this using
-the <a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
-<li>Enable <a href="{@docRoot}tools/device.html#device-developer-options">
-developer options</a> on the test device.</li>
-<li>Install your app on the test device.</li>
-<li>Launch the Android Media Browser simulator to see how your audio app
-appears in Auto. If your app does not appear, stop the simulator from
-<strong>Settings &gt; Apps</strong> then restart it.</li>
+  <li>Enable developer mode on your mobile device, as described in
+    <a href="{@docRoot}tools/device.html#developer-device-options">Enabling On-device
+    Developer Options</a>. </li>
+  <li>Compile your app in your development environment and install your app on
+    a physical mobile device running Android 5.0 (API level 21) or higher. To check the
+    version of Android on a Nexus device, go to
+    <strong>Settings &gt; About phone</strong> (or <strong>About tablet</strong>) <strong>&gt;
+    Android version</strong>.</li>
+
+  <li>Install the
+     <a class="external-link"
+     href="https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead&hl=en"
+     >Android Auto app</a> on the mobile device.</li>
+  <li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and
+    download the DHU package <strong>Android Auto Desktop Head Unit</strong> from the
+    <em>SDK Tools</em> tab. The DHU installs in the <code>&lt;sdk&gt;/extras/google/auto/</code>
+    directory.</li>
+  <li>If you are running the DHU on Linux, you must also install
+    the portaudio, libpng, sdl2, and sdl2_ttf libraries.
+    The procedure to do this varies depending on your Linux distribution. For example, on
+    Debian-derived Linux distributions, you can install the libraries with this command:
+
+<pre class="no-pretty-print">
+$ sudo apt-get install libsdl2-2.0-0 libsdl2-ttf-2.0-0 libportaudio2 libpng12-0
+</pre>
+
+  </li>
 </ol>
 
-<h3 id="testing-messaging-apps">Testing messaging apps</h3>
-<p>To run and test messaging apps:</p>
+<div class="figure" style="width:330px">
+  <img src="{@docRoot}images/training/auto-desktop-head-unit-server-running.png"
+      alt="" >
+  <p class="img-caption">
+    <strong>Figure 2.</strong> Notification that the head unit server is running.
+  </p>
+</div>
+<img src="{@docRoot}images/training/auto-desktop-head-unit-context-menu-enabled.png"
+    alt="" >
+<p class="img-caption">
+  <strong>Figure 1.</strong> Context menu with developer options.
+</p>
+
+<h3 id="connecting-dhu">Connecting the DHU to your mobile device</h3>
+
+<p>Run the DHU by connecting your mobile device to a development machine and setting up a connection to
+  the head unit server over <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge
+  (ADB)</a>. Follow these steps to set up tunneling and start the DHU:</p>
 
 <ol>
-<li>Install the Android Messaging simulator
-  ({@code &lt;sdk&gt;/extras/google/simulators/messaging-simulator.apk})
-on the test device. You can do this using the
-<a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
-<li>Enable the simulator to read notifications posted on the system:
-<ol type="a">
-	<li>Enable <a href="{@docRoot}tools/device.html#device-developer-options">
-developer options</a> on the test device.</li>
-	<li>Click <strong>Settings &gt; Sounds &amp; Notifications &gt; Notification
-	Access</strong> and check the box labeled
-	<strong>Messaging Simulator</strong>.</li>
+  <li>On the mobile device, enable Android Auto developer mode by starting the Android Auto
+    companion app, and then tapping the header image 10 times.
+    This step is only required the first time you run the companion app.
+  </li>
+  <li>If the server is not already running, select <strong>Start head unit server</strong>
+     from the Android Auto menu.
+    <p>On the device, a foreground service appears in the notification area. </p>
+  </li>
+
+  <li>Connect the mobile device to the development machine via USB. Your device must be unlocked to
+    launch the DHU.
+  </li>
+  <li>On the development machine, run the following {@code adb} command to
+    forward socket connections from the
+    development machine's port 5277 to the same port number on the Android device.
+    This configuration allows the DHU to connect to the head unit server running on your phone over
+    a TCP socket.
+    <pre class="no-pretty-print">$ adb forward tcp:5277 tcp:5277</pre>
+  </li>
+
+  <li>Start the DHU by running the command <code>desktop-head-unit.exe</code> (on Windows)
+    or <code>./desktop-head-unit</code> (on Mac or Linux) from the
+    <code>&lt;sdk&gt;/extras/google/auto/</code> directory.
+
+<pre class="no-pretty-print">$ cd &lt;sdk&gt;/extras/google/auto
+$ ./desktop-head-unit</pre>
+
+    <p>
+      By default, the head unit server connects over port 5277. To override the host or port
+      (for example, to forward over SSH), use the <code>--adb</code> flag.
+    </p>
+
+  </li>
 </ol>
-<li>Install your app on the test device.</li>
-<li>Launch the Android Messaging Simulator to see how your messaging app appears
-in Auto. If your app does not appear, stop the simulator from
-<strong>Settings &gt; Apps</strong> then restart it.</li>
-</ol>
+
+<div class="figure" style="width:432px">
+
+    <img src="{@docRoot}images/training/auto-desktop-head-unit-wkst-launch.png"
+        alt="" >
+    <p class="img-caption">
+      <strong>Figure 4.</strong> DHU launches on the development machine.
+    </p>
+</div>
+
+    <img src="{@docRoot}images/training/auto-desktop-head-unit-launch.png"
+        alt="" >
+    <p class="img-caption">
+      <strong>Figure 3.</strong> Android Auto launches on the mobile device.
+    </p>
+
+<p>
+  After you set up and start the DHU, you can run DHU commands from the command line to run and
+  test your app from the terminal. You can also run these commands by using keyboard shortcuts. For
+  more information about DHU configuration and commands, see <a href=
+  "{@docRoot}tools/help/desktop-head-unit.html">Desktop Head Unit</a>.
+</p>
+
+
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 1c14e2f..c7aa8c3 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -48,8 +48,8 @@
     // Software rendering properties.
     private float mOpacity = 0;
 
-    public RippleBackground(RippleDrawable owner, Rect bounds) {
-        super(owner, bounds);
+    public RippleBackground(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+        super(owner, bounds, forceSoftware);
     }
 
     public boolean isVisible() {
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 23a3ee3..2d378c6 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -52,9 +52,16 @@
     /** Screen density used to adjust pixel-based constants. */
     protected float mDensity;
 
-    public RippleComponent(RippleDrawable owner, Rect bounds) {
+    /**
+     * If set, force all ripple animations to not run on RenderThread, even if it would be
+     * available.
+     */
+    private final boolean mForceSoftware;
+
+    public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
         mOwner = owner;
         mBounds = bounds;
+        mForceSoftware = forceSoftware;
     }
 
     public void onBoundsChange() {
@@ -143,7 +150,7 @@
      * @return {@code true} if something was drawn, {@code false} otherwise
      */
     public boolean draw(Canvas c, Paint p) {
-        final boolean hasDisplayListCanvas = c.isHardwareAccelerated()
+        final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated()
                 && c instanceof DisplayListCanvas;
         if (mHasDisplayListCanvas != hasDisplayListCanvas) {
             mHasDisplayListCanvas = hasDisplayListCanvas;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 464f3de..2690223 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -166,6 +166,12 @@
     private boolean mOverrideBounds;
 
     /**
+     * If set, force all ripple animations to not run on RenderThread, even if it would be
+     * available.
+     */
+    private boolean mForceSoftware;
+
+    /**
      * Constructor used for drawable inflation.
      */
     RippleDrawable() {
@@ -546,7 +552,7 @@
      */
     private void tryBackgroundEnter(boolean focused) {
         if (mBackground == null) {
-            mBackground = new RippleBackground(this, mHotspotBounds);
+            mBackground = new RippleBackground(this, mHotspotBounds, mForceSoftware);
         }
 
         mBackground.setup(mState.mMaxRadius, mDensity);
@@ -584,7 +590,7 @@
             }
 
             final boolean isBounded = isBounded();
-            mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
+            mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded, mForceSoftware);
         }
 
         mRipple.setup(mState.mMaxRadius, mDensity);
@@ -949,6 +955,16 @@
         }
     }
 
+    /**
+     * Sets whether to disable RenderThread animations for this ripple.
+     *
+     * @param forceSoftware true if RenderThread animations should be disabled, false otherwise
+     * @hide
+     */
+    public void setForceSoftware(boolean forceSoftware) {
+        mForceSoftware = forceSoftware;
+    }
+
     @Override
     public ConstantState getConstantState() {
         return mState;
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 4853b04..c660846 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -87,8 +87,8 @@
     private boolean mHasFinishedExit;
 
     public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
-            boolean isBounded) {
-        super(owner, bounds);
+            boolean isBounded, boolean forceSoftware) {
+        super(owner, bounds, forceSoftware);
 
         mIsBounded = isBounded;
         mStartingX = startingX;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index f0ed0bb..e9dde29 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -33,7 +33,9 @@
     // Get the number of available CPUs. This value does not change over time.
     int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
 
-    int workerCount = MathUtils::max(1, cpuCount / 2);
+    // Really no point in making more than 2 of these worker threads, but
+    // we do want to limit ourselves to 1 worker thread on dual-core devices.
+    int workerCount = cpuCount > 2 ? 2 : 1;
     for (int i = 0; i < workerCount; i++) {
         String8 name;
         name.appendFormat("hwuiTask%d", i + 1);
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
index da33464..bc2dae1 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -31,8 +31,7 @@
  */
 public final class ActivityRecognitionProvider {
     private final IActivityRecognitionHardware mService;
-    private final HashSet<Sink> mSinkSet = new HashSet<Sink>();
-    private final SinkTransport mSinkTransport = new SinkTransport();
+    private final HashSet<Sink> mSinkSet = new HashSet<>();
 
     // the following constants must remain in sync with activity_recognition.h
 
@@ -43,6 +42,8 @@
     public static final String ACTIVITY_STILL = "android.activity_recognition.still";
     public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting";
 
+    // NOTE: when adding an additional EVENT_TYPE_, EVENT_TYPE_COUNT needs to be updated in
+    // android.hardware.location.ActivityRecognitionHardware
     public static final int EVENT_TYPE_FLUSH_COMPLETE = 0;
     public static final int EVENT_TYPE_ENTER = 1;
     public static final int EVENT_TYPE_EXIT = 2;
@@ -60,7 +61,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(service);
         mService = service;
-        mService.registerSink(mSinkTransport);
+        mService.registerSink(new SinkTransport());
     }
 
     public String[] getSupportedActivities() throws RemoteException {
@@ -102,26 +103,23 @@
 
     private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub {
         @Override
-        public void onActivityChanged(
-                android.hardware.location.ActivityChangedEvent activityChangedEvent) {
+        public void onActivityChanged(android.hardware.location.ActivityChangedEvent event) {
             Collection<Sink> sinks;
             synchronized (mSinkSet) {
                 if (mSinkSet.isEmpty()) {
                     return;
                 }
-
-                sinks = new ArrayList<Sink>(mSinkSet);
+                sinks = new ArrayList<>(mSinkSet);
             }
 
             // translate the event from platform internal and GmsCore types
-            ArrayList<ActivityRecognitionEvent> gmsEvents =
-                    new ArrayList<ActivityRecognitionEvent>();
-            for (android.hardware.location.ActivityRecognitionEvent event
-                    : activityChangedEvent.getActivityRecognitionEvents()) {
+            ArrayList<ActivityRecognitionEvent> gmsEvents = new ArrayList<>();
+            for (android.hardware.location.ActivityRecognitionEvent reportingEvent
+                    : event.getActivityRecognitionEvents()) {
                 ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent(
-                        event.getActivity(),
-                        event.getEventType(),
-                        event.getTimestampNs());
+                        reportingEvent.getActivity(),
+                        reportingEvent.getEventType(),
+                        reportingEvent.getTimestampNs());
                 gmsEvents.add(gmsEvent);
             }
             ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents);
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
new file mode 100644
index 0000000..0b878d7
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.location.provider;
+
+import android.annotation.NonNull;
+import android.hardware.location.IActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A client class for interaction with an Activity-Recognition provider.
+ */
+public abstract class ActivityRecognitionProviderClient {
+    private static final String TAG = "ArProviderClient";
+
+    protected ActivityRecognitionProviderClient() {}
+
+    private IActivityRecognitionHardwareClient.Stub mClient =
+            new IActivityRecognitionHardwareClient.Stub() {
+                @Override
+                public void onAvailabilityChanged(
+                        boolean isSupported,
+                        IActivityRecognitionHardware instance) {
+                    int callingUid = Binder.getCallingUid();
+                    if (callingUid != Process.SYSTEM_UID) {
+                        Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid);
+                        return;
+                    }
+                    ActivityRecognitionProvider provider;
+                    try {
+                        provider = isSupported ? new ActivityRecognitionProvider(instance) : null;
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error creating Hardware Activity-Recognition Provider.", e);
+                        return;
+                    }
+                    onProviderChanged(isSupported, provider);
+                }
+            };
+
+    /**
+     * Gets the binder needed to interact with proxy provider in the platform.
+     */
+    @NonNull
+    public IBinder getBinder() {
+        return mClient;
+    }
+
+    /**
+     * Called when a change in the availability of {@link ActivityRecognitionProvider} is detected.
+     *
+     * @param isSupported whether the platform supports the provider natively
+     * @param instance the available provider's instance
+     */
+    public abstract void onProviderChanged(
+            boolean isSupported,
+            ActivityRecognitionProvider instance);
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
index 03dd042..7139025 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
@@ -28,7 +28,10 @@
 
 /**
  * A watcher class for Activity-Recognition instances.
+ *
+ * @deprecated use {@link ActivityRecognitionProviderClient} instead.
  */
+@Deprecated
 public class ActivityRecognitionProviderWatcher {
     private static final String TAG = "ActivityRecognitionProviderWatcher";
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 0f1be6b..e92f294 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -671,15 +671,15 @@
             case USAGE_VOICE_COMMUNICATION:
                 return new String("USAGE_VOICE_COMMUNICATION");
             case USAGE_VOICE_COMMUNICATION_SIGNALLING:
-                return new String("USAGE_VOICE_COMMUNICATION");
+                return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING");
             case USAGE_ALARM:
                 return new String("USAGE_ALARM");
             case USAGE_NOTIFICATION:
                 return new String("USAGE_NOTIFICATION");
             case USAGE_NOTIFICATION_RINGTONE:
-                return new String("USAGE_NOTIFICATION");
+                return new String("USAGE_NOTIFICATION_RINGTONE");
             case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
-                return new String("USAGE_NOTIFICATION");
+                return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST");
             case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
                 return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT");
             case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 26b41e8..76c701a 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -38,10 +38,6 @@
 using android::String16;
 
 /*****************************************************************************/
-
-android::Mutex android::SensorManager::sLock;
-std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
-
 ASensorManager* ASensorManager_getInstance()
 {
     return ASensorManager_getInstanceForPackage(NULL);
@@ -206,4 +202,4 @@
 bool ASensor_isWakeUpSensor(ASensor const* sensor)
 {
     return static_cast<Sensor const*>(sensor)->isWakeUpSensor();
-}
\ No newline at end of file
+}
diff --git a/packages/Keyguard/res/values/config.xml b/packages/Keyguard/res/values/config.xml
index 8d9d6ee..b398ab2 100644
--- a/packages/Keyguard/res/values/config.xml
+++ b/packages/Keyguard/res/values/config.xml
@@ -22,4 +22,10 @@
 
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
+
+    <!-- Threshold in micro amperes below which a charger is rated as "slow" -->
+    <integer name="config_chargingSlowlyThreshold">1000000</integer>
+
+    <!-- Threshold in micro amperes above which a charger is rated as "fast" -->
+    <integer name="config_chargingFastThreshold">1500000</integer>
 </resources>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index cba122f..d1e84f5 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -56,6 +56,14 @@
          is not fully charged, say that it's charging.  -->
     <string name="keyguard_plugged_in">Charging</string>
 
+    <!-- When the lock screen is showing and the phone plugged in, and the battery
+         is not fully charged, and it's plugged into a fast charger, say that it's charging fast.  -->
+    <string name="keyguard_plugged_in_charging_fast">Charging rapidly</string>
+
+    <!-- When the lock screen is showing and the phone plugged in, and the battery
+         is not fully charged, and it's plugged into a slow charger, say that it's charging slowly.  -->
+    <string name="keyguard_plugged_in_charging_slowly">Charging slowly</string>
+
     <!-- When the lock screen is showing and the battery is low, warn user to plug
          in the phone soon. -->
     <string name="keyguard_low_battery">Connect your charger.</string>
@@ -308,6 +316,15 @@
     <!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_restart_password">Password required when you restart device.</string>
 
+    <!-- An explanation text that the pattern needs to be solved since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security.</string>
+
+    <!-- An explanation text that the pin needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_timeout_pin">PIN required for additional security.</string>
+
+    <!-- An explanation text that the password needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_timeout_password">Password required for additional security.</string>
+
     <!-- An explanation text that the pattern needs to be solved since profiles have just been switched. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles.</string>
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 8f792de..b03871a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -40,6 +40,7 @@
     protected SecurityMessageDisplay mSecurityMessageDisplay;
     protected View mEcaView;
     protected boolean mEnableHaptics;
+    private boolean mDismissing;
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     // any passwords with length less than or equal to this length.
@@ -67,6 +68,7 @@
     @Override
     public void reset() {
         // start fresh
+        mDismissing = false;
         resetPasswordText(false /* animate */);
         // if the user is currently locked out, enforce it.
         long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
@@ -113,6 +115,8 @@
     }
 
     protected void verifyPasswordAndUnlock() {
+        if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
         final String entry = getPasswordText();
         setPasswordEntryInputEnabled(false);
         if (mPendingLockCheck != null) {
@@ -143,6 +147,7 @@
 
     private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) {
         if (matched) {
+            mDismissing = true;
             mCallback.reportUnlockAttempt(true, 0);
             mCallback.dismiss(true);
         } else {
@@ -234,6 +239,12 @@
         }
     }
 
+    @Override
+    public void showMessage(String message, int color) {
+        mSecurityMessageDisplay.setNextMessageColor(color);
+        mSecurityMessageDisplay.setMessage(message, true /* important */);
+    }
+
     protected abstract int getPromtReasonStringRes(int reason);
 
     // Cause a VIRTUAL_KEY vibration
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index ff4e815..ec2a173 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -163,13 +163,18 @@
      * Show a string explaining why the security view needs to be solved.
      *
      * @param reason a flag indicating which string should be shown, see
-     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE}
-     *               and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
+     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
+     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
      */
     public void showPromptReason(int reason) {
         mSecurityContainer.showPromptReason(reason);
     }
 
+    public void showMessage(String message, int color) {
+        mSecurityContainer.showMessage(message, color);
+    }
+
     /**
      *  Dismisses the keyguard by going to the next screen or making it gone.
      *
@@ -209,9 +214,12 @@
     /**
      * Authentication has happened and it's time to dismiss keyguard. This function
      * should clean up and inform KeyguardViewMediator.
+     *
+     * @param strongAuth whether the user has authenticated with strong authentication like
+     *                   pattern, password or PIN but not by trust agents or fingerprint
      */
     @Override
-    public void finish() {
+    public void finish(boolean strongAuth) {
         // If there's a pending runnable because the user interacted with a widget
         // and we're leaving keyguard, then run it.
         boolean deferKeyguardDone = false;
@@ -222,9 +230,9 @@
         }
         if (mViewMediatorCallback != null) {
             if (deferKeyguardDone) {
-                mViewMediatorCallback.keyguardDonePending();
+                mViewMediatorCallback.keyguardDonePending(strongAuth);
             } else {
-                mViewMediatorCallback.keyguardDone(true);
+                mViewMediatorCallback.keyguardDone(strongAuth);
             }
         }
     }
@@ -281,32 +289,6 @@
     }
 
     /**
-     * Verify that the user can get past the keyguard securely.  This is called,
-     * for example, when the phone disables the keyguard but then wants to launch
-     * something else that requires secure access.
-     *
-     * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
-     */
-    public void verifyUnlock() {
-        SecurityMode securityMode = mSecurityContainer.getSecurityMode();
-        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
-            if (mViewMediatorCallback != null) {
-                mViewMediatorCallback.keyguardDone(true);
-            }
-        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
-                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
-                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
-            // can only verify unlock when in pattern/password mode
-            if (mViewMediatorCallback != null) {
-                mViewMediatorCallback.keyguardDone(false);
-            }
-        } else {
-            // otherwise, go to the unlock screen, see if they can verify it
-            mSecurityContainer.verifyUnlock();
-        }
-    }
-
-    /**
      * Called before this view is being removed.
      */
     public void cleanUp() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 2951af9..c8adf64 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -39,15 +39,18 @@
      * lift-to-type from interrupting itself.
      */
     private static final long ANNOUNCEMENT_DELAY = 250;
+    private static final int DEFAULT_COLOR = -1;
 
     private static final int SECURITY_MESSAGE_DURATION = 5000;
 
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final Handler mHandler;
+    private final int mDefaultColor;
 
     // Timeout before we reset the message to show charging/owner info
     long mTimeout = SECURITY_MESSAGE_DURATION;
     CharSequence mMessage;
+    private int mNextMessageColor = DEFAULT_COLOR;
 
     private final Runnable mClearMessageRunnable = new Runnable() {
         @Override
@@ -78,10 +81,16 @@
         mUpdateMonitor.registerCallback(mInfoCallback);
         mHandler = new Handler(Looper.myLooper());
 
+        mDefaultColor = getCurrentTextColor();
         update();
     }
 
     @Override
+    public void setNextMessageColor(int color) {
+        mNextMessageColor = color;
+    }
+
+    @Override
     public void setMessage(CharSequence msg, boolean important) {
         if (!TextUtils.isEmpty(msg) && important) {
             securityMessageChanged(msg);
@@ -151,6 +160,12 @@
         CharSequence status = mMessage;
         setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
         setText(status);
+        int color = mDefaultColor;
+        if (mNextMessageColor != DEFAULT_COLOR) {
+            color = mNextMessageColor;
+            mNextMessageColor = DEFAULT_COLOR;
+        }
+        setTextColor(color);
     }
 
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 2db87b3..3a7e6d0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -115,6 +115,8 @@
         switch (reason) {
             case PROMPT_REASON_RESTART:
                 return R.string.kg_prompt_reason_restart_password;
+            case PROMPT_REASON_TIMEOUT:
+                return R.string.kg_prompt_reason_timeout_password;
             default:
                 return 0;
         }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 4bd1a2e..9a91ca4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,10 +15,6 @@
  */
 package com.android.keyguard;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.AsyncTask;
@@ -28,7 +24,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
-import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
@@ -337,11 +332,22 @@
                 mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern,
                         true /* important */);
                 break;
+            case PROMPT_REASON_TIMEOUT:
+                mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern,
+                        true /* important */);
+                break;
             default:
+                break;
         }
     }
 
     @Override
+    public void showMessage(String message, int color) {
+        mSecurityMessageDisplay.setNextMessageColor(color);
+        mSecurityMessageDisplay.setMessage(message, true /* important */);
+    }
+
+    @Override
     public void startAppearAnimation() {
         enableClipping(false);
         setAlpha(1f);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 07947b1..ce2d11a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -62,6 +62,7 @@
         return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
     }
 
+    @Override
     protected void resetState() {
         mPasswordEntry.setEnabled(true);
     }
@@ -69,11 +70,13 @@
     @Override
     protected void setPasswordEntryEnabled(boolean enabled) {
         mPasswordEntry.setEnabled(enabled);
+        mOkButton.setEnabled(enabled);
     }
 
     @Override
     protected void setPasswordEntryInputEnabled(boolean enabled) {
         mPasswordEntry.setEnabled(enabled);
+        mOkButton.setEnabled(enabled);
     }
 
     @Override
@@ -98,6 +101,8 @@
         switch (reason) {
             case PROMPT_REASON_RESTART:
                 return R.string.kg_prompt_reason_restart_pin;
+            case PROMPT_REASON_TIMEOUT:
+                return R.string.kg_prompt_reason_timeout_pin;
             default:
                 return 0;
         }
@@ -184,6 +189,7 @@
         mDeleteButton = findViewById(R.id.delete_button);
         mDeleteButton.setVisibility(View.VISIBLE);
         mDeleteButton.setOnClickListener(new OnClickListener() {
+            @Override
             public void onClick(View v) {
                 // check for time-based lockouts
                 if (mPasswordEntry.isEnabled()) {
@@ -193,6 +199,7 @@
             }
         });
         mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
             public boolean onLongClick(View v) {
                 // check for time-based lockouts
                 if (mPasswordEntry.isEnabled()) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 85da298..77215a7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -54,7 +54,12 @@
         public boolean dismiss(boolean authenticated);
         public void userActivity();
         public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
-        public void finish();
+
+        /**
+         * @param strongAuth wheher the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
+         */
+        public void finish(boolean strongAuth);
         public void reset();
     }
 
@@ -282,7 +287,7 @@
                 showWipeDialog(failedAttempts, userType);
             }
         }
-        monitor.reportFailedUnlockAttempt();
+        monitor.reportFailedStrongAuthUnlockAttempt();
         mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
         if (timeoutMs > 0) {
             showTimeoutDialog(timeoutMs);
@@ -308,6 +313,7 @@
     boolean showNextSecurityScreenOrFinish(boolean authenticated) {
         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
         boolean finish = false;
+        boolean strongAuth = false;
         if (mUpdateMonitor.getUserCanSkipBouncer(
                 KeyguardUpdateMonitor.getCurrentUser())) {
             finish = true;
@@ -323,6 +329,7 @@
                 case Pattern:
                 case Password:
                 case PIN:
+                    strongAuth = true;
                     finish = true;
                     break;
 
@@ -346,7 +353,7 @@
             }
         }
         if (finish) {
-            mSecurityCallback.finish();
+            mSecurityCallback.finish(strongAuth);
         }
         return finish;
     }
@@ -518,6 +525,13 @@
         }
     }
 
+
+    public void showMessage(String message, int color) {
+        if (mCurrentSecuritySelection != SecurityMode.None) {
+            getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
+        }
+    }
+
     @Override
     public void showUsabilityHint() {
         mSecurityViewFlipper.showUsabilityHint();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index 5658a74..38302fb 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -22,9 +22,18 @@
     static public final int VIEW_REVEALED = 2;
 
     int PROMPT_REASON_NONE = 0;
+
+    /**
+     * Strong auth is required because the device has just booted.
+     */
     int PROMPT_REASON_RESTART = 1;
 
     /**
+     * Strong auth is required because the user hasn't used strong auth since a while.
+     */
+    int PROMPT_REASON_TIMEOUT = 2;
+
+    /**
      * Interface back to keyguard to tell it when security
      * @param callback
      */
@@ -77,6 +86,14 @@
     void showPromptReason(int reason);
 
     /**
+     * Show a message on the security view with a specified color
+     *
+     * @param message the message to show
+     * @param color the color to use
+     */
+    void showMessage(String message, int color);
+
+    /**
      * Instruct the view to show usability hints, if any.
      *
      */
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index a0ff21b..6012c45 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -139,6 +139,14 @@
     }
 
     @Override
+    public void showMessage(String message, int color) {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.showMessage(message, color);
+        }
+    }
+
+    @Override
     public void showUsabilityHint() {
         KeyguardSecurityView ksv = getSecurityView();
         if (ksv != null) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c7adc98..ead0307 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,46 +29,37 @@
 import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-
-import static android.os.BatteryManager.BATTERY_STATUS_FULL;
-import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
-import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
-import static android.os.BatteryManager.EXTRA_STATUS;
-import static android.os.BatteryManager.EXTRA_PLUGGED;
-import static android.os.BatteryManager.EXTRA_LEVEL;
-import static android.os.BatteryManager.EXTRA_HEALTH;
-
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.media.AudioManager;
 import android.os.BatteryManager;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IRemoteCallback;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
-
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.google.android.collect.Lists;
 
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.widget.LockPatternUtils;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -77,6 +68,15 @@
 import java.util.List;
 import java.util.Map.Entry;
 
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_HEALTH;
+import static android.os.BatteryManager.EXTRA_LEVEL;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
+import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_STATUS;
+
 /**
  * Watches for updates that may be interesting to the keyguard, and provides
  * the up to date information as well as a registration for callbacks that care
@@ -92,32 +92,24 @@
     private static final String TAG = "KeyguardUpdateMonitor";
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
-    private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
     private static final int LOW_BATTERY_THRESHOLD = 20;
-    private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
 
     private static final String ACTION_FACE_UNLOCK_STARTED
             = "com.android.facelock.FACE_UNLOCK_STARTED";
     private static final String ACTION_FACE_UNLOCK_STOPPED
             = "com.android.facelock.FACE_UNLOCK_STOPPED";
-    private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+
+    private static final String ACTION_STRONG_AUTH_TIMEOUT =
+            "com.android.systemui.ACTION_STRONG_AUTH_TIMEOUT";
+    private static final String USER_ID = "com.android.systemui.USER_ID";
+
+    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     /**
-     * Mode in which we don't need to wake up the device when we get a fingerprint.
+     * Milliseconds after unlocking with fingerprint times out, i.e. the user has to use a
+     * strong auth method like password, PIN or pattern.
      */
-    private static final int FP_WAKE_NONE = 0;
-
-    /**
-     * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
-     * a fingerprint while the screen is off and the device was sleeping.
-     */
-    private static final int FP_WAKE_DIRECT_UNLOCK = 1;
-
-    /**
-     * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
-     * acquire a fingerprint pulsing in doze mode.
-     * */
-    private static final int FP_WAKE_WAKE_TO_BOUNCER = 2;
+    private static final long FINGERPRINT_UNLOCK_TIMEOUT_MS = 72 * 60 * 60 * 1000;
 
     // Callback messages
     private static final int MSG_TIME_UPDATE = 301;
@@ -141,6 +133,26 @@
     private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328;
     private static final int MSG_AIRPLANE_MODE_CHANGED = 329;
     private static final int MSG_SERVICE_STATE_CHANGE = 330;
+    private static final int MSG_SCREEN_TURNED_ON = 331;
+    private static final int MSG_SCREEN_TURNED_OFF = 332;
+
+    /** Fingerprint state: Not listening to fingerprint. */
+    private static final int FINGERPRINT_STATE_STOPPED = 0;
+
+    /** Fingerprint state: Listening. */
+    private static final int FINGERPRINT_STATE_RUNNING = 1;
+
+    /**
+     * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to
+     * send us the confirmation that cancellation has happened.
+     */
+    private static final int FINGERPRINT_STATE_CANCELLING = 2;
+
+    /**
+     * Fingerprint state: During cancelling we got another request to start listening, so when we
+     * receive the cancellation done signal, we should start listening again.
+     */
+    private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3;
 
     private static KeyguardUpdateMonitor sInstance;
 
@@ -151,9 +163,14 @@
     private int mRingMode;
     private int mPhoneState;
     private boolean mKeyguardIsVisible;
+
+    /**
+     * If true, fingerprint was already authenticated and we don't need to start listening again
+     * until the Keyguard has been dismissed.
+     */
+    private boolean mFingerprintAlreadyAuthenticated;
     private boolean mBouncer;
     private boolean mBootCompleted;
-    private boolean mUserHasAuthenticatedSinceBoot;
 
     // Device provisioning state
     private boolean mDeviceProvisioned;
@@ -164,7 +181,9 @@
     // Password attempts
     private SparseIntArray mFailedAttempts = new SparseIntArray();
 
-    private boolean mClockVisible;
+    /** Tracks whether strong authentication hasn't been used since quite some time per user. */
+    private ArraySet<Integer> mStrongAuthNotTimedOut = new ArraySet<>();
+    private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker();
 
     private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
             mCallbacks = Lists.newArrayList();
@@ -175,10 +194,10 @@
     private boolean mDeviceInteractive;
     private boolean mScreenOn;
     private SubscriptionManager mSubscriptionManager;
+    private AlarmManager mAlarmManager;
     private List<SubscriptionInfo> mSubscriptionInfo;
-    private boolean mFingerprintDetectionRunning;
     private TrustManager mTrustManager;
-    private PowerManager mPowerManager;
+    private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -247,6 +266,12 @@
                 case MSG_SERVICE_STATE_CHANGE:
                     handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
                     break;
+                case MSG_SCREEN_TURNED_ON:
+                    handleScreenTurnedOn();
+                    break;
+                case MSG_SCREEN_TURNED_OFF:
+                    handleScreenTurnedOff();
+                    break;
             }
         }
     };
@@ -266,8 +291,6 @@
 
     private static int sCurrentUser;
 
-    private int mFpWakeMode;
-
     public synchronized static void setCurrentUser(int currentUser) {
         sCurrentUser = currentUser;
     }
@@ -368,18 +391,28 @@
         }
     }
 
-    private void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+    private void onFingerprintAuthenticated(int userId) {
         mUserFingerprintAuthenticated.put(userId, true);
+
+        // If fingerprint unlocking is allowed, this event will lead to a Keyguard dismiss or to a
+        // wake-up (if Keyguard is not showing), so we don't need to listen until Keyguard is
+        // fully gone.
+        mFingerprintAlreadyAuthenticated = isUnlockingWithFingerprintAllowed();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAuthenticated(userId, wakeAndUnlocking);
+                cb.onFingerprintAuthenticated(userId);
             }
         }
     }
 
     private void handleFingerprintAuthFailed() {
-        releaseFingerprintWakeLock();
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onFingerprintAuthFailed();
+            }
+        }
         handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
     }
 
@@ -387,53 +420,15 @@
         if (acquireInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
             return;
         }
-        if (!mDeviceInteractive && !mScreenOn) {
-            releaseFingerprintWakeLock();
-            mWakeLock = mPowerManager.newWakeLock(
-                    PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
-            mWakeLock.acquire();
-            mFpWakeMode = FP_WAKE_DIRECT_UNLOCK;
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onFingerprintAcquired();
             }
-            mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
-                    FINGERPRINT_WAKELOCK_TIMEOUT_MS);
-        } else if (!mDeviceInteractive) {
-            mFpWakeMode = FP_WAKE_WAKE_TO_BOUNCER;
-        } else {
-            mFpWakeMode = FP_WAKE_NONE;
-        }
-    }
-
-    private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fp wakelock: TIMEOUT!!");
-            }
-            releaseFingerprintWakeLock();
-        }
-    };
-
-    private void releaseFingerprintWakeLock() {
-        if (mWakeLock != null) {
-            mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "releasing fp wakelock");
-            }
-            mWakeLock.release();
-            mWakeLock = null;
         }
     }
 
     private void handleFingerprintAuthenticated() {
-        if (mFpWakeMode == FP_WAKE_WAKE_TO_BOUNCER || mFpWakeMode == FP_WAKE_DIRECT_UNLOCK) {
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fp wakelock: Authenticated, waking up...");
-            }
-            mPowerManager.wakeUp(SystemClock.uptimeMillis());
-        }
-        releaseFingerprintWakeLock();
         try {
             final int userId;
             try {
@@ -446,9 +441,9 @@
                 Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
                 return;
             }
-            onFingerprintAuthenticated(userId, mFpWakeMode == FP_WAKE_DIRECT_UNLOCK);
+            onFingerprintAuthenticated(userId);
         } finally {
-            setFingerprintRunningDetectionRunning(false);
+            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
         }
     }
 
@@ -462,7 +457,13 @@
     }
 
     private void handleFingerprintError(int msgId, String errString) {
-        setFingerprintRunningDetectionRunning(false);
+        if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
+                && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
+            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+            startListeningForFingerprint();
+        } else {
+            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -471,9 +472,15 @@
         }
     }
 
-    private void setFingerprintRunningDetectionRunning(boolean running) {
-        if (running != mFingerprintDetectionRunning) {
-            mFingerprintDetectionRunning = running;
+    private void setFingerprintRunningState(int fingerprintRunningState) {
+        boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+        boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+        mFingerprintRunningState = fingerprintRunningState;
+
+        // Clients of KeyguardUpdateMonitor don't care about the internal state about the
+        // asynchronousness of the cancel cycle. So only notify them if the actualy running state
+        // has changed.
+        if (wasRunning != isRunning) {
             notifyFingerprintRunningStateChanged();
         }
     }
@@ -482,7 +489,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintRunningStateChanged(mFingerprintDetectionRunning);
+                cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning());
             }
         }
     }
@@ -501,7 +508,7 @@
     }
 
     public boolean isFingerprintDetectionRunning() {
-        return mFingerprintDetectionRunning;
+        return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
     }
 
     private boolean isTrustDisabled(int userId) {
@@ -533,7 +540,48 @@
     }
 
     public boolean isUnlockingWithFingerprintAllowed() {
-        return mUserHasAuthenticatedSinceBoot;
+        return mStrongAuthTracker.isUnlockingWithFingerprintAllowed()
+                && !hasFingerprintUnlockTimedOut(sCurrentUser);
+    }
+
+    public StrongAuthTracker getStrongAuthTracker() {
+        return mStrongAuthTracker;
+    }
+
+    /**
+     * @return true if the user hasn't use strong authentication (pattern, PIN, password) since a
+     *         while and thus can't unlock with fingerprint, false otherwise
+     */
+    public boolean hasFingerprintUnlockTimedOut(int userId) {
+        return !mStrongAuthNotTimedOut.contains(userId);
+    }
+
+    public void reportSuccessfulStrongAuthUnlockAttempt() {
+        mStrongAuthNotTimedOut.add(sCurrentUser);
+        scheduleStrongAuthTimeout();
+        if (mFpm != null) {
+            byte[] token = null; /* TODO: pass real auth token once fp HAL supports it */
+            mFpm.resetTimeout(token);
+        }
+    }
+
+    private void scheduleStrongAuthTimeout() {
+        long when = SystemClock.elapsedRealtime() + FINGERPRINT_UNLOCK_TIMEOUT_MS;
+        Intent intent = new Intent(ACTION_STRONG_AUTH_TIMEOUT);
+        intent.putExtra(USER_ID, sCurrentUser);
+        PendingIntent sender = PendingIntent.getBroadcast(mContext,
+                sCurrentUser, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, sender);
+        notifyStrongAuthStateChanged(sCurrentUser);
+    }
+
+    private void notifyStrongAuthStateChanged(int userId) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onStrongAuthStateChanged(userId);
+            }
+        }
     }
 
     static class DisplayClientState {
@@ -561,8 +609,10 @@
                 final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
                 final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
                 final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+                final int maxChargingCurrent = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
                 final Message msg = mHandler.obtainMessage(
-                        MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));
+                        MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
+                        maxChargingCurrent));
                 mHandler.sendMessage(msg);
             } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
                 SimData args = SimData.fromIntent(intent);
@@ -619,6 +669,17 @@
         }
     };
 
+    private final BroadcastReceiver mStrongAuthTimeoutReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_STRONG_AUTH_TIMEOUT.equals(intent.getAction())) {
+                int userId = intent.getIntExtra(USER_ID, -1);
+                mStrongAuthNotTimedOut.remove(userId);
+                notifyStrongAuthStateChanged(userId);
+            }
+        }
+    };
+
     private FingerprintManager.AuthenticationCallback mAuthenticationCallback
             = new AuthenticationCallback() {
 
@@ -649,7 +710,6 @@
     };
     private CancellationSignal mFingerprintCancelSignal;
     private FingerprintManager mFpm;
-    private PowerManager.WakeLock mWakeLock;
 
     /**
      * When we receive a
@@ -719,15 +779,22 @@
     }
 
     public static class BatteryStatus {
+        public static final int CHARGING_UNKNOWN = -1;
+        public static final int CHARGING_SLOWLY = 0;
+        public static final int CHARGING_REGULAR = 1;
+        public static final int CHARGING_FAST = 2;
+
         public final int status;
         public final int level;
         public final int plugged;
         public final int health;
-        public BatteryStatus(int status, int level, int plugged, int health) {
+        public final int maxChargingCurrent;
+        public BatteryStatus(int status, int level, int plugged, int health, int maxChargingCurrent) {
             this.status = status;
             this.level = level;
             this.plugged = plugged;
             this.health = health;
+            this.maxChargingCurrent = maxChargingCurrent;
         }
 
         /**
@@ -758,6 +825,31 @@
             return level < LOW_BATTERY_THRESHOLD;
         }
 
+        public final int getChargingSpeed(int slowThreshold, int fastThreshold) {
+            return maxChargingCurrent <= 0 ? CHARGING_UNKNOWN :
+                    maxChargingCurrent < slowThreshold ? CHARGING_SLOWLY :
+                    maxChargingCurrent > fastThreshold ? CHARGING_FAST :
+                    CHARGING_REGULAR;
+        }
+    }
+
+    public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+
+        public boolean isUnlockingWithFingerprintAllowed() {
+            int userId = getCurrentUser();
+            return isFingerprintAllowedForUser(userId);
+        }
+
+        public boolean hasUserAuthenticatedSinceBoot() {
+            int userId = getCurrentUser();
+            return (getStrongAuthForUser(userId)
+                    & STRONG_AUTH_REQUIRED_AFTER_BOOT) == 0;
+        }
+
+        @Override
+        public void onStrongAuthRequiredChanged(int userId) {
+            notifyStrongAuthStateChanged(userId);
+        }
     }
 
     public static KeyguardUpdateMonitor getInstance(Context context) {
@@ -787,9 +879,30 @@
                 cb.onFinishedGoingToSleep(arg1);
             }
         }
+        mFingerprintAlreadyAuthenticated = false;
         updateFingerprintListeningState();
     }
 
+    private void handleScreenTurnedOn() {
+        final int count = mCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onScreenTurnedOn();
+            }
+        }
+    }
+
+    private void handleScreenTurnedOff() {
+        final int count = mCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onScreenTurnedOff();
+            }
+        }
+    }
+
     /**
      * IMPORTANT: Must be called from UI thread.
      */
@@ -816,8 +929,9 @@
     private KeyguardUpdateMonitor(Context context) {
         mContext = context;
         mSubscriptionManager = SubscriptionManager.from(context);
-        mPowerManager = context.getSystemService(PowerManager.class);
+        mAlarmManager = context.getSystemService(AlarmManager.class);
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
+
         // Since device can't be un-provisioned, we only need to register a content observer
         // to update mDeviceProvisioned when we are...
         if (!mDeviceProvisioned) {
@@ -825,7 +939,7 @@
         }
 
         // Take a guess at initial SIM state, battery status and PLMN until we get an update
-        mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0);
+        mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0);
 
         // Watch for interesting updates
         final IntentFilter filter = new IntentFilter();
@@ -878,8 +992,13 @@
             e.printStackTrace();
         }
 
+        IntentFilter strongAuthTimeoutFilter = new IntentFilter();
+        strongAuthTimeoutFilter.addAction(ACTION_STRONG_AUTH_TIMEOUT);
+        context.registerReceiver(mStrongAuthTimeoutReceiver, strongAuthTimeoutFilter,
+                PERMISSION_SELF, null /* handler */);
         mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
         mTrustManager.registerTrustListener(this);
+        new LockPatternUtils(context).registerStrongAuthTracker(mStrongAuthTracker);
 
         mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
         updateFingerprintListeningState();
@@ -887,44 +1006,51 @@
 
     private void updateFingerprintListeningState() {
         boolean shouldListenForFingerprint = shouldListenForFingerprint();
-        if (mFingerprintDetectionRunning && !shouldListenForFingerprint) {
+        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
             stopListeningForFingerprint();
-        } else if (!mFingerprintDetectionRunning && shouldListenForFingerprint) {
+        } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
+                && shouldListenForFingerprint) {
             startListeningForFingerprint();
         }
     }
 
     private boolean shouldListenForFingerprint() {
-        return mKeyguardIsVisible && !mSwitchingUser;
+        return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser
+                && !mFingerprintAlreadyAuthenticated;
     }
 
     private void startListeningForFingerprint() {
+        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
+            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
+            return;
+        }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
         int userId = ActivityManager.getCurrentUser();
-        if (isUnlockWithFingerPrintPossible(userId)) {
-            mUserHasAuthenticatedSinceBoot = mTrustManager.hasUserAuthenticatedSinceBoot(
-                    ActivityManager.getCurrentUser());
+        if (isUnlockWithFingerprintPossible(userId)) {
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
             mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
-            setFingerprintRunningDetectionRunning(true);
+            setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
         }
     }
 
-    public boolean isUnlockWithFingerPrintPossible(int userId) {
+    public boolean isUnlockWithFingerprintPossible(int userId) {
         return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
                 && mFpm.getEnrolledFingerprints(userId).size() > 0;
     }
 
     private void stopListeningForFingerprint() {
         if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
-        if (isFingerprintDetectionRunning()) {
+        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
             mFingerprintCancelSignal.cancel();
             mFingerprintCancelSignal = null;
+            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
         }
-        setFingerprintRunningDetectionRunning(false);
+        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
+            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+        }
     }
 
     private boolean isDeviceProvisionedInSettingsDb() {
@@ -1193,6 +1319,9 @@
                 cb.onKeyguardVisibilityChangedRaw(isShowing);
             }
         }
+        if (!isShowing) {
+            mFingerprintAlreadyAuthenticated = false;
+        }
         updateFingerprintListeningState();
     }
 
@@ -1255,6 +1384,12 @@
         if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
             return true;
         }
+
+        // change in charging current while plugged in
+        if (nowPluggedIn && current.maxChargingCurrent != old.maxChargingCurrent) {
+            return true;
+        }
+
         return false;
     }
 
@@ -1373,7 +1508,7 @@
         return mFailedAttempts.get(sCurrentUser, 0);
     }
 
-    public void reportFailedUnlockAttempt() {
+    public void reportFailedStrongAuthUnlockAttempt() {
         mFailedAttempts.put(sCurrentUser, getFailedUnlockAttempts() + 1);
     }
 
@@ -1464,12 +1599,14 @@
         synchronized (this) {
             mScreenOn = true;
         }
+        mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON);
     }
 
     public void dispatchScreenTurnedOff() {
         synchronized(this) {
             mScreenOn = false;
         }
+        mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_OFF);
     }
 
     public boolean isDeviceInteractive() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 0cdf999..15ffe9f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -161,6 +161,16 @@
     public void onFinishedGoingToSleep(int why) { }
 
     /**
+     * Called when the screen has been turned on.
+     */
+    public void onScreenTurnedOn() { }
+
+    /**
+     * Called when the screen has been turned off.
+     */
+    public void onScreenTurnedOff() { }
+
+    /**
      * Called when trust changes for a user.
      */
     public void onTrustChanged(int userId) { }
@@ -176,12 +186,23 @@
     public void onTrustGrantedWithFlags(int flags, int userId) { }
 
     /**
+     * Called when a finger has been acquired.
+     * <p>
+     * It is guaranteed that either {@link #onFingerprintAuthenticated} or
+     * {@link #onFingerprintAuthFailed()} is called after this method eventually.
+     */
+    public void onFingerprintAcquired() { }
+
+    /**
+     * Called when a fingerprint couldn't be authenticated.
+     */
+    public void onFingerprintAuthFailed() { }
+
+    /**
      * Called when a fingerprint is recognized.
      * @param userId the user id for which the fingerprint was authenticated
-     * @param wakeAndUnlocking whether the authentication woke the device up and thus we'd like to
-     *                         dismiss the lockscreen before turning on the screen
      */
-    public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) { }
+    public void onFingerprintAuthenticated(int userId) { }
 
     /**
      * Called when fingerprint provides help string (e.g. "Try again")
@@ -207,4 +228,10 @@
      * Called when the fingerprint running state changed.
      */
     public void onFingerprintRunningStateChanged(boolean running) { }
+
+    /**
+     * Called when the state that the user hasn't used strong authentication since quite some time
+     * has changed.
+     */
+    public void onStrongAuthStateChanged(int userId) { }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
index b38cfd5..ddb1f6e 100644
--- a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -17,11 +17,14 @@
 package com.android.keyguard;
 
 public interface SecurityMessageDisplay {
-    public void setMessage(CharSequence msg, boolean important);
 
-    public void setMessage(int resId, boolean important);
+    void setNextMessageColor(int color);
 
-    public void setMessage(int resId, boolean important, Object... formatArgs);
+    void setMessage(CharSequence msg, boolean important);
 
-    public void setTimeout(int timeout_ms);
+    void setMessage(int resId, boolean important);
+
+    void setMessage(int resId, boolean important, Object... formatArgs);
+
+    void setTimeout(int timeout_ms);
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
index ff463c6..8ab3011 100644
--- a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
@@ -28,12 +28,11 @@
 
     /**
      * Report that the keyguard is done.
-     * @param authenticated Whether the user securely got past the keyguard.
-     *   the only reason for this to be false is if the keyguard was instructed
-     *   to appear temporarily to verify the user is supposed to get past the
-     *   keyguard, and the user fails to do so.
+     *
+     * @param strongAuth whether the user has authenticated with strong authentication like
+     *                   pattern, password or PIN but not by trust agents or fingerprint
      */
-    void keyguardDone(boolean authenticated);
+    void keyguardDone(boolean strongAuth);
 
     /**
      * Report that the keyguard is done drawing.
@@ -48,8 +47,11 @@
 
     /**
      * Report that the keyguard is dismissable, pending the next keyguardDone call.
+     *
+     * @param strongAuth whether the user has authenticated with strong authentication like
+     *                   pattern, password or PIN but not by trust agents or fingerprint
      */
-    void keyguardDonePending();
+    void keyguardDonePending(boolean strongAuth);
 
     /**
      * Report when keyguard is actually gone
@@ -85,8 +87,9 @@
     /**
      * @return one of the reasons why the bouncer needs to be shown right now and the user can't use
      *         his normal unlock method like fingerprint or trust agents. See
-     *         {@link KeyguardSecurityView#PROMPT_REASON_NONE}
-     *         and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}.
+     *         {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+     *         {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
+     *         {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
      */
     int getBouncerPromptReason();
 }
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 1c4b05f..3ad8f21 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -21,4 +21,6 @@
 
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
-</resources>
\ No newline at end of file
+
+    <dimen name="circle_avatar_size">40dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b03f100..9b1f103 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -195,4 +195,34 @@
     <!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wifi_signal_full">Wifi signal full.</string>
 
+    <!-- Label for kernel threads in battery usage -->
+    <string name="process_kernel_label">Android OS</string>
+    <!-- Title of data usage item that represents all uninstalled applications. [CHAR LIMIT=48] -->
+    <string name="data_usage_uninstalled_apps">Removed apps</string>
+    <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] -->
+    <string name="data_usage_uninstalled_apps_users">Removed apps and users</string>
+
+    <!-- Tethering controls, item title to go into the tethering settings -->
+    <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_usb">USB tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only Wifi tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_wifi">Portable hotspot</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only Bluetooth tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_bluetooth">Bluetooth tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_usb_bluetooth">Tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_all">Tethering &amp; portable hotspot</string>
+
+    <!-- Title for a work profile. [CHAR LIMIT=25] -->
+    <string name="managed_user_title">Work profile</string>
+
+    <!-- Title for Guest user [CHAR LIMIT=35] -->
+    <string name="user_guest">Guest</string>
+
+    <!-- Manage apps, individual app screen, substituted for the application's label when the app's label CAN NOT be determined.-->
+    <string name="unknown">Unknown</string>
+
+    <!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
+    <string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AppItem.java b/packages/SettingsLib/src/com/android/settingslib/AppItem.java
new file mode 100644
index 0000000..1729e09
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/AppItem.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseBooleanArray;
+
+public class AppItem implements Comparable<AppItem>, Parcelable {
+    public static final int CATEGORY_USER = 0;
+    public static final int CATEGORY_APP_TITLE = 1;
+    public static final int CATEGORY_APP = 2;
+
+    public final int key;
+    public boolean restricted;
+    public int category;
+
+    public SparseBooleanArray uids = new SparseBooleanArray();
+    public long total;
+
+    public AppItem() {
+        this.key = 0;
+    }
+
+    public AppItem(int key) {
+        this.key = key;
+    }
+
+    public AppItem(Parcel parcel) {
+        key = parcel.readInt();
+        uids = parcel.readSparseBooleanArray();
+        total = parcel.readLong();
+    }
+
+    public void addUid(int uid) {
+        uids.put(uid, true);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(key);
+        dest.writeSparseBooleanArray(uids);
+        dest.writeLong(total);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int compareTo(AppItem another) {
+        int comparison = Integer.compare(category, another.category);
+        if (comparison == 0) {
+            comparison = Long.compare(another.total, total);
+        }
+        return comparison;
+    }
+
+    public static final Creator<AppItem> CREATOR = new Creator<AppItem>() {
+        @Override
+        public AppItem createFromParcel(Parcel in) {
+            return new AppItem(in);
+        }
+
+        @Override
+        public AppItem[] newArray(int size) {
+            return new AppItem[size];
+        }
+    };
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
new file mode 100644
index 0000000..cf08f50
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import static android.net.NetworkPolicy.CYCLE_NONE;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.SNOOZE_NEVER;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkTemplate.MATCH_WIFI;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.net.wifi.WifiInfo;
+import android.os.AsyncTask;
+import android.text.TextUtils;
+import android.text.format.Time;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to modify list of {@link NetworkPolicy}. Specifically knows
+ * about which policies can coexist. This editor offers thread safety when
+ * talking with {@link NetworkPolicyManager}.
+ *
+ * @hide
+ */
+public class NetworkPolicyEditor {
+    // TODO: be more robust when missing policies from service
+
+    public static final boolean ENABLE_SPLIT_POLICIES = false;
+
+    private NetworkPolicyManager mPolicyManager;
+    private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
+
+    public NetworkPolicyEditor(NetworkPolicyManager policyManager) {
+        mPolicyManager = checkNotNull(policyManager);
+    }
+
+    public void read() {
+        final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
+
+        boolean modified = false;
+        mPolicies.clear();
+        for (NetworkPolicy policy : policies) {
+            // TODO: find better place to clamp these
+            if (policy.limitBytes < -1) {
+                policy.limitBytes = LIMIT_DISABLED;
+                modified = true;
+            }
+            if (policy.warningBytes < -1) {
+                policy.warningBytes = WARNING_DISABLED;
+                modified = true;
+            }
+
+            mPolicies.add(policy);
+        }
+
+        // when we cleaned policies above, write back changes
+        if (modified) writeAsync();
+    }
+
+    public void writeAsync() {
+        // TODO: consider making more robust by passing through service
+        final NetworkPolicy[] policies = mPolicies.toArray(new NetworkPolicy[mPolicies.size()]);
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                write(policies);
+                return null;
+            }
+        }.execute();
+    }
+
+    public void write(NetworkPolicy[] policies) {
+        mPolicyManager.setNetworkPolicies(policies);
+    }
+
+    public boolean hasLimitedPolicy(NetworkTemplate template) {
+        final NetworkPolicy policy = getPolicy(template);
+        return policy != null && policy.limitBytes != LIMIT_DISABLED;
+    }
+
+    public NetworkPolicy getOrCreatePolicy(NetworkTemplate template) {
+        NetworkPolicy policy = getPolicy(template);
+        if (policy == null) {
+            policy = buildDefaultPolicy(template);
+            mPolicies.add(policy);
+        }
+        return policy;
+    }
+
+    public NetworkPolicy getPolicy(NetworkTemplate template) {
+        for (NetworkPolicy policy : mPolicies) {
+            if (policy.template.equals(template)) {
+                return policy;
+            }
+        }
+        return null;
+    }
+
+    public NetworkPolicy getPolicyMaybeUnquoted(NetworkTemplate template) {
+        NetworkPolicy policy = getPolicy(template);
+        if (policy != null) {
+            return policy;
+        } else {
+            return getPolicy(buildUnquotedNetworkTemplate(template));
+        }
+    }
+
+    @Deprecated
+    private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) {
+        // TODO: move this into framework to share with NetworkPolicyManagerService
+        final int cycleDay;
+        final String cycleTimezone;
+        final boolean metered;
+
+        if (template.getMatchRule() == MATCH_WIFI) {
+            cycleDay = CYCLE_NONE;
+            cycleTimezone = Time.TIMEZONE_UTC;
+            metered = false;
+        } else {
+            final Time time = new Time();
+            time.setToNow();
+            cycleDay = time.monthDay;
+            cycleTimezone = time.timezone;
+            metered = true;
+        }
+
+        return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED,
+                LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true);
+    }
+
+    public int getPolicyCycleDay(NetworkTemplate template) {
+        final NetworkPolicy policy = getPolicy(template);
+        return (policy != null) ? policy.cycleDay : -1;
+    }
+
+    public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
+        final NetworkPolicy policy = getOrCreatePolicy(template);
+        policy.cycleDay = cycleDay;
+        policy.cycleTimezone = cycleTimezone;
+        policy.inferred = false;
+        policy.clearSnooze();
+        writeAsync();
+    }
+
+    public long getPolicyWarningBytes(NetworkTemplate template) {
+        final NetworkPolicy policy = getPolicy(template);
+        return (policy != null) ? policy.warningBytes : WARNING_DISABLED;
+    }
+
+    public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
+        final NetworkPolicy policy = getOrCreatePolicy(template);
+        policy.warningBytes = warningBytes;
+        policy.inferred = false;
+        policy.clearSnooze();
+        writeAsync();
+    }
+
+    public long getPolicyLimitBytes(NetworkTemplate template) {
+        final NetworkPolicy policy = getPolicy(template);
+        return (policy != null) ? policy.limitBytes : LIMIT_DISABLED;
+    }
+
+    public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) {
+        final NetworkPolicy policy = getOrCreatePolicy(template);
+        policy.limitBytes = limitBytes;
+        policy.inferred = false;
+        policy.clearSnooze();
+        writeAsync();
+    }
+
+    public boolean getPolicyMetered(NetworkTemplate template) {
+        NetworkPolicy policy = getPolicy(template);
+        if (policy != null) {
+            return policy.metered;
+        } else {
+            return false;
+        }
+    }
+
+    public void setPolicyMetered(NetworkTemplate template, boolean metered) {
+        boolean modified = false;
+
+        NetworkPolicy policy = getPolicy(template);
+        if (metered) {
+            if (policy == null) {
+                policy = buildDefaultPolicy(template);
+                policy.metered = true;
+                policy.inferred = false;
+                mPolicies.add(policy);
+                modified = true;
+            } else if (!policy.metered) {
+                policy.metered = true;
+                policy.inferred = false;
+                modified = true;
+            }
+
+        } else {
+            if (policy == null) {
+                // ignore when policy doesn't exist
+            } else if (policy.metered) {
+                policy.metered = false;
+                policy.inferred = false;
+                modified = true;
+            }
+        }
+
+        // Remove legacy unquoted policies while we're here
+        final NetworkTemplate unquoted = buildUnquotedNetworkTemplate(template);
+        final NetworkPolicy unquotedPolicy = getPolicy(unquoted);
+        if (unquotedPolicy != null) {
+            mPolicies.remove(unquotedPolicy);
+            modified = true;
+        }
+
+        if (modified) writeAsync();
+    }
+
+    /**
+     * Build a revised {@link NetworkTemplate} that matches the same rule, but
+     * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work
+     * around legacy bugs.
+     */
+    private static NetworkTemplate buildUnquotedNetworkTemplate(NetworkTemplate template) {
+        if (template == null) return null;
+        final String networkId = template.getNetworkId();
+        final String strippedNetworkId = WifiInfo.removeDoubleQuotes(networkId);
+        if (!TextUtils.equals(strippedNetworkId, networkId)) {
+            return new NetworkTemplate(
+                    template.getMatchRule(), template.getSubscriberId(), strippedNetworkId);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index 58e5e29a..5062423 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -54,29 +54,7 @@
     public static boolean setWifiTethering(boolean enable, Context context) {
         final WifiManager wifiManager =
                 (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        final ContentResolver cr = context.getContentResolver();
-        /**
-         * Disable Wifi if enabling tethering
-         */
-        int wifiState = wifiManager.getWifiState();
-        if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
-                    (wifiState == WifiManager.WIFI_STATE_ENABLED))) {
-            wifiManager.setWifiEnabled(false);
-            Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);
-        }
-
-        boolean success = wifiManager.setWifiApEnabled(null, enable);
-        /**
-         *  If needed, restore Wifi on tether disable
-         */
-        if (!enable) {
-            int wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
-            if (wifiSavedState == 1) {
-                wifiManager.setWifiEnabled(true);
-                Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
-            }
-        }
-        return success;
+        return wifiManager.setWifiApEnabled(null, enable);
     }
 
     public static boolean isWifiTetherEnabled(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
new file mode 100644
index 0000000..621a09cd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -0,0 +1,84 @@
+package com.android.settingslib;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+public final class Utils {
+
+    /**
+     * Return string resource that best describes combination of tethering
+     * options available on this device.
+     */
+    public static int getTetheringLabel(ConnectivityManager cm) {
+        String[] usbRegexs = cm.getTetherableUsbRegexs();
+        String[] wifiRegexs = cm.getTetherableWifiRegexs();
+        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+        boolean usbAvailable = usbRegexs.length != 0;
+        boolean wifiAvailable = wifiRegexs.length != 0;
+        boolean bluetoothAvailable = bluetoothRegexs.length != 0;
+
+        if (wifiAvailable && usbAvailable && bluetoothAvailable) {
+            return R.string.tether_settings_title_all;
+        } else if (wifiAvailable && usbAvailable) {
+            return R.string.tether_settings_title_all;
+        } else if (wifiAvailable && bluetoothAvailable) {
+            return R.string.tether_settings_title_all;
+        } else if (wifiAvailable) {
+            return R.string.tether_settings_title_wifi;
+        } else if (usbAvailable && bluetoothAvailable) {
+            return R.string.tether_settings_title_usb_bluetooth;
+        } else if (usbAvailable) {
+            return R.string.tether_settings_title_usb;
+        } else {
+            return R.string.tether_settings_title_bluetooth;
+        }
+    }
+
+    /**
+     * Returns a label for the user, in the form of "User: user name" or "Work profile".
+     */
+    public static String getUserLabel(Context context, UserInfo info) {
+        String name = info != null ? info.name : null;
+        if (info.isManagedProfile()) {
+            // We use predefined values for managed profiles
+            return context.getString(R.string.managed_user_title);
+        } else if (info.isGuest()) {
+            name = context.getString(R.string.user_guest);
+        }
+        if (name == null && info != null) {
+            name = Integer.toString(info.id);
+        } else if (info == null) {
+            name = context.getString(R.string.unknown);
+        }
+        return context.getResources().getString(R.string.running_process_item_user_label, name);
+    }
+
+    /**
+     * Returns a circular icon for a user.
+     */
+    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+        if (user.isManagedProfile()) {
+            // We use predefined values for managed profiles
+            Bitmap b = BitmapFactory.decodeResource(context.getResources(),
+                    com.android.internal.R.drawable.ic_corp_icon);
+            return CircleFramedDrawable.getInstance(context, b);
+        }
+        if (user.iconPath != null) {
+            Bitmap icon = um.getUserIcon(user.id);
+            if (icon != null) {
+                return CircleFramedDrawable.getInstance(context, icon);
+            }
+        }
+        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
+                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
new file mode 100644
index 0000000..278b57d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.drawable;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one.
+ * TODO: Move this to an internal framework class and share with the one in Keyguard.
+ */
+public class CircleFramedDrawable extends Drawable {
+
+    private final Bitmap mBitmap;
+    private final int mSize;
+    private final Paint mPaint;
+
+    private float mScale;
+    private Rect mSrcRect;
+    private RectF mDstRect;
+
+    public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
+        Resources res = context.getResources();
+        float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+
+        CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+        return instance;
+    }
+
+    public CircleFramedDrawable(Bitmap icon, int size) {
+        super();
+        mSize = size;
+
+        mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(mBitmap);
+
+        final int width = icon.getWidth();
+        final int height = icon.getHeight();
+        final int square = Math.min(width, height);
+
+        final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+        final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+
+        final Path fillPath = new Path();
+        fillPath.addArc(circleRect, 0f, 360f);
+
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        // opaque circle matte
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setColor(Color.BLACK);
+        mPaint.setStyle(Paint.Style.FILL);
+        canvas.drawPath(fillPath, mPaint);
+
+        // mask in the icon where the bitmap is opaque
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+
+        // prepare paint for frame drawing
+        mPaint.setXfermode(null);
+
+        mScale = 1f;
+
+        mSrcRect = new Rect(0, 0, mSize, mSize);
+        mDstRect = new RectF(0, 0, mSize, mSize);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final float inside = mScale * mSize;
+        final float pad = (mSize - inside) / 2f;
+
+        mDstRect.set(pad, pad, mSize - pad, mSize - pad);
+        canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+    }
+
+    public void setScale(float scale) {
+        mScale = scale;
+    }
+
+    public float getScale() {
+        return mScale;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+    }
+    
+    @Override
+    public int getIntrinsicWidth() {
+        return mSize;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mSize;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
new file mode 100644
index 0000000..e30aac5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.net.NetworkStatsHistory;
+
+public class ChartData {
+    public NetworkStatsHistory network;
+
+    public NetworkStatsHistory detail;
+    public NetworkStatsHistory detailDefault;
+    public NetworkStatsHistory detailForeground;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
new file mode 100644
index 0000000..223c055
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.settingslib.AppItem;
+
+/**
+ * Loader for historical chart data for both network and UID details.
+ */
+public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
+    private static final String KEY_TEMPLATE = "template";
+    private static final String KEY_APP = "app";
+    private static final String KEY_FIELDS = "fields";
+
+    private final INetworkStatsSession mSession;
+    private final Bundle mArgs;
+
+    public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
+        return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
+    }
+
+    public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
+        final Bundle args = new Bundle();
+        args.putParcelable(KEY_TEMPLATE, template);
+        args.putParcelable(KEY_APP, app);
+        args.putInt(KEY_FIELDS, fields);
+        return args;
+    }
+
+    public ChartDataLoader(Context context, INetworkStatsSession session, Bundle args) {
+        super(context);
+        mSession = session;
+        mArgs = args;
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        forceLoad();
+    }
+
+    @Override
+    public ChartData loadInBackground() {
+        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+        final AppItem app = mArgs.getParcelable(KEY_APP);
+        final int fields = mArgs.getInt(KEY_FIELDS);
+
+        try {
+            return loadInBackground(template, app, fields);
+        } catch (RemoteException e) {
+            // since we can't do much without history, and we don't want to
+            // leave with half-baked UI, we bail hard.
+            throw new RuntimeException("problem reading network stats", e);
+        }
+    }
+
+    private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
+            throws RemoteException {
+        final ChartData data = new ChartData();
+        data.network = mSession.getHistoryForNetwork(template, fields);
+
+        if (app != null) {
+            // load stats for current uid and template
+            final int size = app.uids.size();
+            for (int i = 0; i < size; i++) {
+                final int uid = app.uids.keyAt(i);
+                data.detailDefault = collectHistoryForUid(
+                        template, uid, SET_DEFAULT, data.detailDefault);
+                data.detailForeground = collectHistoryForUid(
+                        template, uid, SET_FOREGROUND, data.detailForeground);
+            }
+
+            if (size > 0) {
+                data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
+                data.detail.recordEntireHistory(data.detailDefault);
+                data.detail.recordEntireHistory(data.detailForeground);
+            } else {
+                data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
+                data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
+                data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
+            }
+        }
+
+        return data;
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        cancelLoad();
+    }
+
+    /**
+     * Collect {@link NetworkStatsHistory} for the requested UID, combining with
+     * an existing {@link NetworkStatsHistory} if provided.
+     */
+    private NetworkStatsHistory collectHistoryForUid(
+            NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
+            throws RemoteException {
+        final NetworkStatsHistory history = mSession.getHistoryForUid(
+                template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
+
+        if (existing != null) {
+            existing.recordEntireHistory(history);
+            return existing;
+        } else {
+            return history;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
new file mode 100644
index 0000000..572bae1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
+    private static final String KEY_TEMPLATE = "template";
+    private static final String KEY_START = "start";
+    private static final String KEY_END = "end";
+
+    private final INetworkStatsSession mSession;
+    private final Bundle mArgs;
+
+    public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
+        final Bundle args = new Bundle();
+        args.putParcelable(KEY_TEMPLATE, template);
+        args.putLong(KEY_START, start);
+        args.putLong(KEY_END, end);
+        return args;
+    }
+
+    public SummaryForAllUidLoader(Context context, INetworkStatsSession session, Bundle args) {
+        super(context);
+        mSession = session;
+        mArgs = args;
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        forceLoad();
+    }
+
+    @Override
+    public NetworkStats loadInBackground() {
+        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+        final long start = mArgs.getLong(KEY_START);
+        final long end = mArgs.getLong(KEY_END);
+
+        try {
+            return mSession.getSummaryForAllUid(template, start, end, false);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        cancelLoad();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
new file mode 100644
index 0000000..5e42281
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.graphics.drawable.Drawable;
+
+public class UidDetail {
+    public CharSequence label;
+    public CharSequence contentDescription;
+    public CharSequence[] detailLabels;
+    public CharSequence[] detailContentDescriptions;
+    public Drawable icon;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
new file mode 100644
index 0000000..224b967
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.TrafficStats;
+import android.os.UserManager;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+
+/**
+ * Return details about a specific UID, handling special cases like
+ * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
+ */
+public class UidDetailProvider {
+    private static final String TAG = "DataUsage";
+    private final Context mContext;
+    private final SparseArray<UidDetail> mUidDetailCache;
+
+    public static final int OTHER_USER_RANGE_START = -2000;
+
+    public static int buildKeyForUser(int userHandle) {
+        return OTHER_USER_RANGE_START - userHandle;
+    }
+
+    public static boolean isKeyForUser(int key) {
+        return key <= OTHER_USER_RANGE_START;
+    }
+
+    public static int getUserIdForKey(int key) {
+        return OTHER_USER_RANGE_START - key;
+    }
+
+    public UidDetailProvider(Context context) {
+        mContext = context.getApplicationContext();
+        mUidDetailCache = new SparseArray<UidDetail>();
+    }
+
+    public void clearCache() {
+        synchronized (mUidDetailCache) {
+            mUidDetailCache.clear();
+        }
+    }
+
+    /**
+     * Resolve best descriptive label for the given UID.
+     */
+    public UidDetail getUidDetail(int uid, boolean blocking) {
+        UidDetail detail;
+
+        synchronized (mUidDetailCache) {
+            detail = mUidDetailCache.get(uid);
+        }
+
+        if (detail != null) {
+            return detail;
+        } else if (!blocking) {
+            return null;
+        }
+
+        detail = buildUidDetail(uid);
+
+        synchronized (mUidDetailCache) {
+            mUidDetailCache.put(uid, detail);
+        }
+
+        return detail;
+    }
+
+    /**
+     * Build {@link UidDetail} object, blocking until all {@link Drawable}
+     * lookup is finished.
+     */
+    private UidDetail buildUidDetail(int uid) {
+        final Resources res = mContext.getResources();
+        final PackageManager pm = mContext.getPackageManager();
+
+        final UidDetail detail = new UidDetail();
+        detail.label = pm.getNameForUid(uid);
+        detail.icon = pm.getDefaultActivityIcon();
+
+        // handle special case labels
+        switch (uid) {
+            case android.os.Process.SYSTEM_UID:
+                detail.label = res.getString(R.string.process_kernel_label);
+                detail.icon = pm.getDefaultActivityIcon();
+                return detail;
+            case TrafficStats.UID_REMOVED:
+                detail.label = res.getString(UserManager.supportsMultipleUsers()
+                        ? R.string.data_usage_uninstalled_apps_users
+                        : R.string.data_usage_uninstalled_apps);
+                detail.icon = pm.getDefaultActivityIcon();
+                return detail;
+            case TrafficStats.UID_TETHERING:
+                final ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                        Context.CONNECTIVITY_SERVICE);
+                detail.label = res.getString(Utils.getTetheringLabel(cm));
+                detail.icon = pm.getDefaultActivityIcon();
+                return detail;
+        }
+
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+        // Handle keys that are actually user handles
+        if (isKeyForUser(uid)) {
+            final int userHandle = getUserIdForKey(uid);
+            final UserInfo info = um.getUserInfo(userHandle);
+            if (info != null) {
+                detail.label = Utils.getUserLabel(mContext, info);
+                detail.icon = Utils.getUserIcon(mContext, um, info);
+                return detail;
+            }
+        }
+
+        // otherwise fall back to using packagemanager labels
+        final String[] packageNames = pm.getPackagesForUid(uid);
+        final int length = packageNames != null ? packageNames.length : 0;
+        try {
+            final int userId = UserHandle.getUserId(uid);
+            UserHandle userHandle = new UserHandle(userId);
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            if (length == 1) {
+                final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0],
+                        0 /* no flags */, userId);
+                if (info != null) {
+                    detail.label = info.loadLabel(pm).toString();
+                    detail.icon = um.getBadgedIconForUser(info.loadIcon(pm),
+                            new UserHandle(userId));
+                }
+            } else if (length > 1) {
+                detail.detailLabels = new CharSequence[length];
+                detail.detailContentDescriptions = new CharSequence[length];
+                for (int i = 0; i < length; i++) {
+                    final String packageName = packageNames[i];
+                    final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
+                    final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName,
+                            0 /* no flags */, userId);
+
+                    if (appInfo != null) {
+                        detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
+                        detail.detailContentDescriptions[i] = um.getBadgedLabelForUser(
+                                detail.detailLabels[i], userHandle);
+                        if (packageInfo.sharedUserLabel != 0) {
+                            detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
+                                    packageInfo.applicationInfo).toString();
+                            detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle);
+                        }
+                    }
+                }
+            }
+            detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+        }
+
+        if (TextUtils.isEmpty(detail.label)) {
+            detail.label = Integer.toString(uid);
+        }
+
+        return detail;
+    }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c00fdf3..37e0db0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -104,6 +104,7 @@
     <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
     <uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
     <uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
+    <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
 
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 677ab91..372fa03 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -108,6 +108,7 @@
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
+    <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
 
     <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
     <uses-permission android:name="android.permission.SET_WALLPAPER"/>
diff --git a/packages/SystemUI/res/anim/navbar_fade_in.xml b/packages/SystemUI/res/anim/navbar_fade_in.xml
index e3429e6..7051730 100644
--- a/packages/SystemUI/res/anim/navbar_fade_in.xml
+++ b/packages/SystemUI/res/anim/navbar_fade_in.xml
@@ -19,4 +19,5 @@
     android:fromAlpha="0.0"
     android:toAlpha="1.0"
     android:interpolator="@android:interpolator/linear_out_slow_in"
+    android:startDelay="32"
     android:duration="200"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 260d81b1..123ff78 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -260,12 +260,6 @@
     <!-- Doze: pulse parameter - how long does it take to fade in after a pickup? -->
     <integer name="doze_pulse_duration_in_pickup">300</integer>
 
-    <!-- Doze: pulse parameter - delay to wait for the screen to wake up -->
-    <integer name="doze_pulse_delay_in">200</integer>
-
-    <!-- Doze: pulse parameter - delay to wait for the screen to wake up after a pickup -->
-    <integer name="doze_pulse_delay_in_pickup">200</integer>
-
     <!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
     <integer name="doze_pulse_duration_visible">3000</integer>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8eef23e..13128b7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -37,6 +37,7 @@
     <item type="id" name="doze_saved_filter_tag"/>
     <item type="id" name="qs_icon_tag"/>
     <item type="id" name="scrim"/>
+    <item type="id" name="scrim_target"/>
     <item type="id" name="hun_scrim_alpha_start"/>
     <item type="id" name="hun_scrim_alpha_end"/>
     <item type="id" name="notification_power"/>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 89a2c74..82a1bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -29,6 +29,7 @@
     void stopDozing();
     boolean isPowerSaveActive();
     boolean isNotificationLightOn();
+    boolean isPulsingBlocked();
 
     public interface Callback {
         void onNewNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 887391c..630d735 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -255,6 +255,11 @@
     }
 
     private void continuePulsing(int reason) {
+        if (mHost.isPulsingBlocked()) {
+            mPulsing = false;
+            mWakeLock.release();
+            return;
+        }
         mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
             @Override
             public void onPulseStarted() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d78800f..d2c60ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -80,7 +80,8 @@
         @Override // Binder interface
         public void keyguardDone(boolean authenticated, boolean wakeup) {
             checkPermission();
-            mKeyguardViewMediator.keyguardDone(authenticated, wakeup);
+            // TODO: Remove wakeup
+            mKeyguardViewMediator.keyguardDone(authenticated);
         }
 
         @Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index a1c8b1a..647b272 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -68,6 +68,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -176,11 +177,6 @@
      */
     private static final String KEYGUARD_ANALYTICS_SETTING = "keyguard_analytics";
 
-    /**
-     * How much faster we collapse the lockscreen when authenticating with fingerprint.
-     */
-    private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.3f;
-
     /** The stream type that the lock sounds are tied to. */
     private int mUiSoundsStreamType;
 
@@ -458,30 +454,6 @@
                     break;
             }
         }
-
-        @Override
-        public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
-            boolean unlockingWithFingerprintAllowed =
-                    mUpdateMonitor.isUnlockingWithFingerprintAllowed();
-            if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-                if (unlockingWithFingerprintAllowed) {
-                    mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated();
-                }
-            } else {
-                if (wakeAndUnlocking && mShowing && unlockingWithFingerprintAllowed) {
-                    mWakeAndUnlocking = true;
-                    mStatusBarKeyguardViewManager.setWakeAndUnlocking();
-                    keyguardDone(true, true);
-                } else if (mShowing && mDeviceInteractive) {
-                    if (wakeAndUnlocking) {
-                        mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
-                    }
-                    mStatusBarKeyguardViewManager.animateCollapsePanels(
-                            FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
-                }
-            }
-        };
-
     };
 
     ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -490,9 +462,12 @@
             KeyguardViewMediator.this.userActivity();
         }
 
-        public void keyguardDone(boolean authenticated) {
+        public void keyguardDone(boolean strongAuth) {
             if (!mKeyguardDonePending) {
-                KeyguardViewMediator.this.keyguardDone(authenticated, true);
+                KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
+            }
+            if (strongAuth) {
+                mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
             }
         }
 
@@ -506,12 +481,15 @@
         }
 
         @Override
-        public void keyguardDonePending() {
+        public void keyguardDonePending(boolean strongAuth) {
             mKeyguardDonePending = true;
             mHideAnimationRun = true;
             mStatusBarKeyguardViewManager.startPreHideAnimation(null /* finishRunnable */);
             mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT,
                     KEYGUARD_DONE_PENDING_TIMEOUT_MS);
+            if (strongAuth) {
+                mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
+            }
         }
 
         @Override
@@ -524,7 +502,7 @@
             if (mKeyguardDonePending) {
                 // Somebody has called keyguardDonePending before, which means that we are
                 // authenticated
-                KeyguardViewMediator.this.keyguardDone(true /* authenticated */, true /* wakeUp */);
+                KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
             }
         }
 
@@ -552,9 +530,12 @@
         public int getBouncerPromptReason() {
             int currentUser = ActivityManager.getCurrentUser();
             if ((mUpdateMonitor.getUserTrustIsManaged(currentUser)
-                    || mUpdateMonitor.isUnlockWithFingerPrintPossible(currentUser))
-                    && !mTrustManager.hasUserAuthenticatedSinceBoot(currentUser)) {
+                    || mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser))
+                    && !mUpdateMonitor.getStrongAuthTracker().hasUserAuthenticatedSinceBoot()) {
                 return KeyguardSecurityView.PROMPT_REASON_RESTART;
+            } else if (mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser)
+                    && mUpdateMonitor.hasFingerprintUnlockTimedOut(currentUser)) {
+                return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
             }
             return KeyguardSecurityView.PROMPT_REASON_NONE;
         }
@@ -1189,10 +1170,10 @@
         }
     };
 
-    public void keyguardDone(boolean authenticated, boolean wakeup) {
-        if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
+    public void keyguardDone(boolean authenticated) {
+        if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated +")");
         EventLog.writeEvent(70000, 2);
-        Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0, wakeup ? 1 : 0);
+        Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0);
         mHandler.sendMessage(msg);
     }
 
@@ -1235,14 +1216,11 @@
                     handleNotifyStartedWakingUp();
                     break;
                 case KEYGUARD_DONE:
-                    handleKeyguardDone(msg.arg1 != 0, msg.arg2 != 0);
+                    handleKeyguardDone(msg.arg1 != 0);
                     break;
                 case KEYGUARD_DONE_DRAWING:
                     handleKeyguardDoneDrawing();
                     break;
-                case KEYGUARD_DONE_AUTHENTICATING:
-                    keyguardDone(true, true);
-                    break;
                 case SET_OCCLUDED:
                     handleSetOccluded(msg.arg1 != 0);
                     break;
@@ -1272,7 +1250,7 @@
      * @see #keyguardDone
      * @see #KEYGUARD_DONE
      */
-    private void handleKeyguardDone(boolean authenticated, boolean wakeup) {
+    private void handleKeyguardDone(boolean authenticated) {
         if (DEBUG) Log.d(TAG, "handleKeyguardDone");
         synchronized (this) {
             resetKeyguardDonePendingLocked();
@@ -1586,6 +1564,7 @@
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
             mStatusBarKeyguardViewManager.onScreenTurnedOff();
+            mWakeAndUnlocking = false;
         }
     }
 
@@ -1612,11 +1591,17 @@
         }
     }
 
+    public void onWakeAndUnlocking() {
+        mWakeAndUnlocking = true;
+        keyguardDone(true /* authenticated */);
+    }
+
     public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
             ViewGroup container, StatusBarWindowManager statusBarWindowManager,
-            ScrimController scrimController) {
+            ScrimController scrimController,
+            FingerprintUnlockController fingerprintUnlockController) {
         mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
-                statusBarWindowManager, scrimController);
+                statusBarWindowManager, scrimController, fingerprintUnlockController);
         return mStatusBarKeyguardViewManager;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 2a84362..adc9b36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.graphics.Typeface;
 import android.media.projection.MediaProjectionManager;
 import android.media.projection.IMediaProjectionManager;
 import android.media.projection.IMediaProjection;
@@ -29,7 +30,14 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.text.BidiFormatter;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.WindowManager;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
@@ -39,6 +47,8 @@
         implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener,
         DialogInterface.OnCancelListener {
     private static final String TAG = "MediaProjectionPermissionActivity";
+    private static final float MAX_APP_NAME_SIZE_PX = 500f;
+    private static final String ELLIPSIS = "\u2026";
 
     private boolean mPermanentGrant;
     private String mPackageName;
@@ -84,11 +94,49 @@
             return;
         }
 
-        String appName = aInfo.loadLabel(packageManager).toString();
+        TextPaint paint = new TextPaint();
+        paint.setTextSize(42);
+
+        String label = aInfo.loadLabel(packageManager).toString();
+
+        // If the label contains new line characters it may push the security
+        // message below the fold of the dialog. Labels shouldn't have new line
+        // characters anyways, so just truncate the message the first time one
+        // is seen.
+        final int labelLength = label.length();
+        int offset = 0;
+        while (offset < labelLength) {
+            final int codePoint = label.codePointAt(offset);
+            final int type = Character.getType(codePoint);
+            if (type == Character.LINE_SEPARATOR
+                    || type == Character.CONTROL
+                    || type == Character.PARAGRAPH_SEPARATOR) {
+                label = label.substring(0, offset) + ELLIPSIS;
+                break;
+            }
+            offset += Character.charCount(codePoint);
+        }
+
+        if (label.isEmpty()) {
+            label = mPackageName;
+        }
+
+        String unsanitizedAppName = TextUtils.ellipsize(label,
+                paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
+        String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
+
+        String actionText = getString(R.string.media_projection_dialog_text, appName);
+        SpannableString message = new SpannableString(actionText);
+
+        int appNameIndex = actionText.indexOf(appName);
+        if (appNameIndex >= 0) {
+            message.setSpan(new StyleSpan(Typeface.BOLD),
+                    appNameIndex, appNameIndex + appName.length(), 0);
+        }
 
         mDialog = new AlertDialog.Builder(this)
                 .setIcon(aInfo.loadIcon(packageManager))
-                .setMessage(getString(R.string.media_projection_dialog_text, appName))
+                .setMessage(message)
                 .setPositiveButton(R.string.media_projection_action_text, this)
                 .setNegativeButton(android.R.string.cancel, this)
                 .setView(R.layout.remember_permission_checkbox)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
index f36019b..e64f6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
@@ -21,7 +21,6 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 8c2ac88..f1550a0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -20,7 +20,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ITaskStackListener;
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b47fb304..d0876fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -33,7 +33,6 @@
 import android.view.ViewStub;
 import android.widget.Toast;
 
-import com.android.internal.logging.MetricsConstants;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 298a1cc..d5c9253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -20,10 +20,8 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
-import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
-import android.app.SearchManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -54,15 +52,12 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
@@ -71,7 +66,6 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsAppWidgetHost;
 
 import java.io.IOException;
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 403af70..7f17885 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -350,6 +350,7 @@
         } else {
             updateBackground();
         }
+        setOutlineAlpha(dark ? 0f : 1f);
      }
 
     public void setShowingLegacyBackground(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7065343..c14f2159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -90,6 +90,7 @@
 import com.android.internal.util.NotificationColorUtil;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.DejankUtils;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SwipeHelper;
@@ -167,13 +168,7 @@
     // on-screen navigation buttons
     protected NavigationBarView mNavigationBarView = null;
 
-    protected Boolean mScreenOn;
-
-    // The second field is a bit different from the first one because it only listens to screen on/
-    // screen of events from Keyguard. We need this so we don't have a race condition with the
-    // broadcast. In the future, we should remove the first field altogether and rename the second
-    // field.
-    protected boolean mScreenOnFromKeyguard;
+    protected boolean mDeviceInteractive;
 
     protected boolean mVisible;
 
@@ -1512,6 +1507,15 @@
             final PendingIntent intent = sbn.getNotification().contentIntent;
             final String notificationKey = sbn.getKey();
 
+            // Mark notification for one frame.
+            row.setJustClicked(true);
+            DejankUtils.postAfterTraversal(new Runnable() {
+                @Override
+                public void run() {
+                    row.setJustClicked(false);
+                }
+            });
+
             if (NOTIFICATION_CLICK_DEBUG) {
                 Log.d(TAG, "Clicked on content of " + notificationKey);
             }
@@ -1619,7 +1623,7 @@
 
     protected void updateVisibleToUser() {
         boolean oldVisibleToUser = mVisibleToUser;
-        mVisibleToUser = mVisible && mScreenOnFromKeyguard;
+        mVisibleToUser = mVisible && mDeviceInteractive;
 
         if (oldVisibleToUser != mVisibleToUser) {
             handleVisibleToUserChanged(mVisibleToUser);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a1b07b5..025451d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -64,6 +64,7 @@
     private static final int MSG_APP_TRANSITION_STARTING    = 21 << MSG_SHIFT;
     private static final int MSG_ASSIST_DISCLOSURE          = 22 << MSG_SHIFT;
     private static final int MSG_START_ASSIST               = 23 << MSG_SHIFT;
+    private static final int MSG_CAMERA_LAUNCH_GESTURE      = 24 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -109,6 +110,7 @@
         public void appTransitionStarting(long startTime, long duration);
         public void showAssistDisclosure();
         public void startAssist(Bundle args);
+        public void onCameraLaunchGestureDetected();
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -293,6 +295,14 @@
         }
     }
 
+    @Override
+    public void onCameraLaunchGestureDetected() {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
+            mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         public void handleMessage(Message msg) {
             final int what = msg.what & MSG_MASK;
@@ -391,6 +401,9 @@
                 case MSG_START_ASSIST:
                     mCallbacks.startAssist((Bundle) msg.obj);
                     break;
+                case MSG_CAMERA_LAUNCH_GESTURE:
+                    mCallbacks.onCameraLaunchGestureDetected();
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b88e5ca..5f01306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -110,6 +111,8 @@
         }
     };
 
+    private boolean mJustClicked;
+
     public NotificationContentView getPrivateLayout() {
         return mPrivateLayout;
     }
@@ -301,6 +304,21 @@
         return mHeadsUpHeight;
     }
 
+    /**
+     * Mark whether this notification was just clicked, i.e. the user has just clicked this
+     * notification in this frame.
+     */
+    public void setJustClicked(boolean justClicked) {
+        mJustClicked = justClicked;
+    }
+
+    /**
+     * @return true if this notification has been clicked in this frame, false otherwise
+     */
+    public boolean wasJustClicked() {
+        return mJustClicked;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index d77e050..a6fc4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -34,6 +34,7 @@
     private final Rect mOutlineRect = new Rect();
     protected final int mRoundedRectCornerRadius;
     private boolean mCustomOutline;
+    private float mOutlineAlpha = 1f;
 
     public ExpandableOutlineView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -50,6 +51,7 @@
                 } else {
                     outline.setRoundRect(mOutlineRect, mRoundedRectCornerRadius);
                 }
+                outline.setAlpha(mOutlineAlpha);
             }
         });
     }
@@ -66,6 +68,11 @@
         invalidateOutline();
     }
 
+    protected void setOutlineAlpha(float alpha) {
+        mOutlineAlpha = alpha;
+        invalidateOutline();
+    }
+
     protected void setOutlineRect(RectF rect) {
         if (rect != null) {
             setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 164c496..8058933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -36,6 +36,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
+
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -79,6 +80,7 @@
     private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
     private boolean mSupportHardware;
     private boolean mFinishing;
+    private boolean mLaunchingAffordance;
 
     private CanvasProperty<Float> mHwCircleRadius;
     private CanvasProperty<Float> mHwCenterX;
@@ -152,7 +154,7 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        mSupportHardware = canvas.isHardwareAccelerated();
+        mSupportHardware = false;//canvas.isHardwareAccelerated();
         drawBackgroundCircle(canvas);
         canvas.save();
         canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
@@ -161,9 +163,11 @@
     }
 
     public void setPreviewView(View v) {
+        View oldPreviewView = mPreviewView;
         mPreviewView = v;
         if (mPreviewView != null) {
-            mPreviewView.setVisibility(INVISIBLE);
+            mPreviewView.setVisibility(mLaunchingAffordance
+                    ? oldPreviewView.getVisibility() : INVISIBLE);
         }
     }
 
@@ -176,7 +180,7 @@
     }
 
     private void drawBackgroundCircle(Canvas canvas) {
-        if (mCircleRadius > 0) {
+        if (mCircleRadius > 0 || mFinishing) {
             if (mFinishing && mSupportHardware) {
                 DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
                 displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
@@ -207,11 +211,12 @@
         cancelAnimator(mPreviewClipper);
         mFinishing = true;
         mCircleStartRadius = mCircleRadius;
-        float maxCircleSize = getMaxCircleSize();
+        final float maxCircleSize = getMaxCircleSize();
         Animator animatorToRadius;
         if (mSupportHardware) {
             initHwProperties();
             animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+            startRtAlphaFadeIn();
         } else {
             animatorToRadius = getAnimatorToRadius(maxCircleSize);
         }
@@ -222,6 +227,8 @@
             public void onAnimationEnd(Animator animation) {
                 mAnimationEndRunnable.run();
                 mFinishing = false;
+                mCircleRadius = maxCircleSize;
+                invalidate();
             }
         });
         animatorToRadius.start();
@@ -241,6 +248,36 @@
         }
     }
 
+    /**
+     * Fades in the Circle on the RenderThread. It's used when finishing the circle when it had
+     * alpha 0 in the beginning.
+     */
+    private void startRtAlphaFadeIn() {
+        if (mCircleRadius == 0 && mPreviewView == null) {
+            Paint modifiedPaint = new Paint(mCirclePaint);
+            modifiedPaint.setColor(mCircleColor);
+            modifiedPaint.setAlpha(0);
+            mHwCirclePaint = CanvasProperty.createPaint(modifiedPaint);
+            RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+                    RenderNodeAnimator.PAINT_ALPHA, 255);
+            animator.setTarget(this);
+            animator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+            animator.setDuration(250);
+            animator.start();
+        }
+    }
+
+    public void instantFinishAnimation() {
+        cancelAnimator(mPreviewClipper);
+        if (mPreviewView != null) {
+            mPreviewView.setClipBounds(null);
+            mPreviewView.setVisibility(View.VISIBLE);
+        }
+        mCircleRadius = getMaxCircleSize();
+        setImageAlpha(0, false);
+        invalidate();
+    }
+
     private void startRtCircleFadeOut(long duration) {
         RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
@@ -443,6 +480,7 @@
     public void setImageAlpha(float alpha, boolean animate, long duration,
             Interpolator interpolator, Runnable runnable) {
         cancelAnimator(mAlphaAnimator);
+        alpha = mLaunchingAffordance ? 0 : alpha;
         int endAlpha = (int) (alpha * 255);
         final Drawable background = getBackground();
         if (!animate) {
@@ -509,4 +547,8 @@
             return false;
         }
     }
+
+    public void setLaunchingAffordance(boolean launchingAffordance) {
+        mLaunchingAffordance = launchingAffordance;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 07a055c..54f91da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,17 +16,13 @@
 
 package com.android.systemui.statusbar;
 
-import com.android.internal.app.IBatteryStats;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.graphics.Color;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Handler;
@@ -39,19 +35,35 @@
 import android.util.Log;
 import android.view.View;
 
+import com.android.internal.app.IBatteryStats;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.statusbar.phone.LockIcon;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
 /**
- * Controls the little text indicator on the keyguard.
+ * Controls the indications and error messages shown on the Keyguard
  */
 public class KeyguardIndicationController {
 
     private static final String TAG = "KeyguardIndicationController";
+    private static final boolean DEBUG_CHARGING_CURRENT = false;
 
     private static final int MSG_HIDE_TRANSIENT = 1;
+    private static final int MSG_CLEAR_FP_MSG = 2;
+    private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
 
     private final Context mContext;
     private final KeyguardIndicationTextView mTextView;
     private final IBatteryStats mBatteryInfo;
 
+    private final int mSlowThreshold;
+    private final int mFastThreshold;
+    private final LockIcon mLockIcon;
+    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
     private String mRestingIndication;
     private String mTransientIndication;
     private int mTransientTextColor;
@@ -59,10 +71,20 @@
 
     private boolean mPowerPluggedIn;
     private boolean mPowerCharged;
+    private int mChargingSpeed;
+    private int mChargingCurrent;
+    private String mMessageToShowOnScreenOn;
 
-    public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView) {
+    public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
+                                        LockIcon lockIcon) {
         mContext = context;
         mTextView = textView;
+        mLockIcon = lockIcon;
+
+        Resources res = context.getResources();
+        mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
+        mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
+
 
         mBatteryInfo = IBatteryStats.Stub.asInterface(
                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
@@ -150,7 +172,11 @@
             return mTransientIndication;
         }
         if (mPowerPluggedIn) {
-            return computePowerIndication();
+            String indication = computePowerIndication();
+            if (DEBUG_CHARGING_CURRENT) {
+                indication += ",  " + (mChargingCurrent / 1000) + " mA";
+            }
+            return indication;
         }
         return mRestingIndication;
     }
@@ -174,7 +200,19 @@
         }
 
         // Fall back to simple charging label.
-        return mContext.getResources().getString(R.string.keyguard_plugged_in);
+        int chargingId;
+        switch (mChargingSpeed) {
+            case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
+                chargingId = R.string.keyguard_plugged_in_charging_fast;
+                break;
+            case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
+                chargingId = R.string.keyguard_plugged_in_charging_slowly;
+                break;
+            default:
+                chargingId = R.string.keyguard_plugged_in;
+                break;
+        }
+        return mContext.getResources().getString(chargingId);
     }
 
     KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
@@ -184,8 +222,68 @@
                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
             mPowerCharged = status.isCharged();
+            mChargingCurrent = status.maxChargingCurrent;
+            mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
             updateIndication();
         }
+
+        @Override
+        public void onFingerprintHelp(int msgId, String helpString) {
+            KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+            if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+                return;
+            }
+            int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+            if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
+            } else if (updateMonitor.isDeviceInteractive()) {
+                mLockIcon.setTransientFpError(true);
+                showTransientIndication(helpString, errorColor);
+                mHandler.removeMessages(MSG_CLEAR_FP_MSG);
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
+                        TRANSIENT_FP_ERROR_TIMEOUT);
+            }
+        }
+
+        @Override
+        public void onFingerprintError(int msgId, String errString) {
+            KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+            if (!updateMonitor.isUnlockingWithFingerprintAllowed()
+                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+                return;
+            }
+            int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+            if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
+            } else if (updateMonitor.isDeviceInteractive()) {
+                    showTransientIndication(errString, errorColor);
+                    // We want to keep this message around in case the screen was off
+                    mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+                    hideTransientIndicationDelayed(5000);
+             } else {
+                    mMessageToShowOnScreenOn = errString;
+            }
+        }
+
+        @Override
+        public void onScreenTurnedOn() {
+            if (mMessageToShowOnScreenOn != null) {
+                int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
+                        null);
+                showTransientIndication(mMessageToShowOnScreenOn, errorColor);
+                // We want to keep this message around in case the screen was off
+                mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+                hideTransientIndicationDelayed(5000);
+                mMessageToShowOnScreenOn = null;
+            }
+        }
+
+        @Override
+        public void onFingerprintRunningStateChanged(boolean running) {
+            if (running) {
+                mMessageToShowOnScreenOn = null;
+            }
+        }
     };
 
     BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -203,7 +301,15 @@
             if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
                 mTransientIndication = null;
                 updateIndication();
+            } else if (msg.what == MSG_CLEAR_FP_MSG) {
+                mLockIcon.setTransientFpError(false);
+                hideTransientIndication();
             }
         }
     };
+
+    public void setStatusBarKeyguardViewManager(
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index 01aa8d1..8688c28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -86,6 +86,9 @@
         if (mBackground != null) {
             mBackground.setCallback(this);
         }
+        if (mBackground instanceof RippleDrawable) {
+            ((RippleDrawable) mBackground).setForceSoftware(true);
+        }
         invalidate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6b167b4..5a2fa3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -50,8 +50,6 @@
         pw.print("    getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true));
         pw.print("    getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false));
         pw.print("    getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true));
-        pw.print("    getPulseInDelay(pickup=false): "); pw.println(getPulseInDelay(false));
-        pw.print("    getPulseInDelay(pickup=true): "); pw.println(getPulseInDelay(true));
         pw.print("    getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
         pw.print("    getPulseOutDuration(): "); pw.println(getPulseOutDuration());
         pw.print("    getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
@@ -80,12 +78,6 @@
                 : getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
     }
 
-    public int getPulseInDelay(boolean pickup) {
-        return pickup
-                ? getInt("doze.pulse.delay.in.pickup", R.integer.doze_pulse_delay_in_pickup)
-                : getInt("doze.pulse.delay.in", R.integer.doze_pulse_delay_in);
-    }
-
     public int getPulseVisibleDuration() {
         return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 3e17328..3ff69c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -100,10 +100,35 @@
         mHandler.post(mPulseIn);
     }
 
+    /**
+     * Aborts pulsing immediately.
+     */
+    public void abortPulsing() {
+        cancelPulsing();
+        if (mDozing) {
+            mScrimController.setDozeBehindAlpha(1f);
+            mScrimController.setDozeInFrontAlpha(1f);
+        }
+    }
+
+    public void onScreenTurnedOn() {
+        if (isPulsing()) {
+            final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
+            startScrimAnimation(true /* inFront */, 0f,
+                    mDozeParameters.getPulseInDuration(pickup),
+                    pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
+                    mPulseInFinished);
+        }
+    }
+
     public boolean isPulsing() {
         return mPulseCallback != null;
     }
 
+    public boolean isDozing() {
+        return mDozing;
+    }
+
     private void cancelPulsing() {
         if (DEBUG) Log.d(TAG, "Cancel pulsing");
 
@@ -138,12 +163,11 @@
 
     private void startScrimAnimation(final boolean inFront, float target, long duration,
             Interpolator interpolator) {
-        startScrimAnimation(inFront, target, duration, interpolator, 0 /* delay */,
-                null /* endRunnable */);
+        startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
     }
 
     private void startScrimAnimation(final boolean inFront, float target, long duration,
-            Interpolator interpolator, long delay, final Runnable endRunnable) {
+            Interpolator interpolator, final Runnable endRunnable) {
         Animator current = getCurrentAnimator(inFront);
         if (current != null) {
             float currentTarget = getCurrentTarget(inFront);
@@ -162,7 +186,6 @@
         });
         anim.setInterpolator(interpolator);
         anim.setDuration(duration);
-        anim.setStartDelay(delay);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -222,12 +245,6 @@
                     + DozeLog.pulseReasonToString(mPulseReason));
             if (!mDozing) return;
             DozeLog.tracePulseStart(mPulseReason);
-            final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
-            startScrimAnimation(true /* inFront */, 0f,
-                    mDozeParameters.getPulseInDuration(pickup),
-                    pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
-                    mDozeParameters.getPulseInDelay(pickup),
-                    mPulseInFinished);
 
             // Signal that the pulse is ready to turn the screen on and draw.
             pulseStarted();
@@ -249,7 +266,7 @@
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
             startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
-                    mPulseOutInterpolator, 0 /* delay */, mPulseOutFinished);
+                    mPulseOutInterpolator, mPulseOutFinished);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
new file mode 100644
index 0000000..4e69999
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+
+/**
+ * Controller which coordinates all the fingerprint unlocking actions with the UI.
+ */
+public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
+
+    private static final String TAG = "FingerprintController";
+    private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
+    private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
+    private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+
+    /**
+     * Mode in which we don't need to wake up the device when we get a fingerprint.
+     */
+    public static final int MODE_NONE = 0;
+
+    /**
+     * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
+     * a fingerprint while the screen is off and the device was sleeping.
+     */
+    public static final int MODE_WAKE_AND_UNLOCK = 1;
+
+    /**
+     * Mode in which we wake the device up, and fade out the Keyguard contents because they were
+     * already visible while pulsing in doze mode.
+     */
+    public static final int MODE_WAKE_AND_UNLOCK_PULSING = 2;
+
+    /**
+     * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
+     * acquire a fingerprint pulsing in doze mode.
+     */
+    public static final int MODE_SHOW_BOUNCER = 3;
+
+    /**
+     * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
+     * fingerprint.
+     * */
+    public static final int MODE_ONLY_WAKE = 4;
+
+    /**
+     * Mode in which fingerprint unlocks the device.
+     */
+    public static final int MODE_UNLOCK = 5;
+
+    /**
+     * Mode in which fingerprint brings up the bouncer because fingerprint unlocking is currently
+     * not allowed.
+     */
+    public static final int MODE_DISMISS_BOUNCER = 6;
+
+    /**
+     * How much faster we collapse the lockscreen when authenticating with fingerprint.
+     */
+    private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.3f;
+
+    private PowerManager mPowerManager;
+    private Handler mHandler = new Handler();
+    private PowerManager.WakeLock mWakeLock;
+    private KeyguardUpdateMonitor mUpdateMonitor;
+    private int mMode;
+    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private StatusBarWindowManager mStatusBarWindowManager;
+    private DozeScrimController mDozeScrimController;
+    private KeyguardViewMediator mKeyguardViewMediator;
+    private ScrimController mScrimController;
+    private PhoneStatusBar mPhoneStatusBar;
+
+    public FingerprintUnlockController(Context context,
+            StatusBarWindowManager statusBarWindowManager,
+            DozeScrimController dozeScrimController,
+            KeyguardViewMediator keyguardViewMediator,
+            ScrimController scrimController,
+            PhoneStatusBar phoneStatusBar) {
+        mPowerManager = context.getSystemService(PowerManager.class);
+        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+        mUpdateMonitor.registerCallback(this);
+        mStatusBarWindowManager = statusBarWindowManager;
+        mDozeScrimController = dozeScrimController;
+        mKeyguardViewMediator = keyguardViewMediator;
+        mScrimController = scrimController;
+        mPhoneStatusBar = phoneStatusBar;
+    }
+
+    public void setStatusBarKeyguardViewManager(
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+    }
+
+    private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG_FP_WAKELOCK) {
+                Log.i(TAG, "fp wakelock: TIMEOUT!!");
+            }
+            releaseFingerprintWakeLock();
+        }
+    };
+
+    private void releaseFingerprintWakeLock() {
+        if (mWakeLock != null) {
+            mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
+            if (DEBUG_FP_WAKELOCK) {
+                Log.i(TAG, "releasing fp wakelock");
+            }
+            mWakeLock.release();
+            mWakeLock = null;
+        }
+    }
+
+    @Override
+    public void onFingerprintAcquired() {
+        releaseFingerprintWakeLock();
+        if (!mUpdateMonitor.isDeviceInteractive()) {
+            mWakeLock = mPowerManager.newWakeLock(
+                    PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
+            mWakeLock.acquire();
+            if (DEBUG_FP_WAKELOCK) {
+                Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+            }
+            mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
+                    FINGERPRINT_WAKELOCK_TIMEOUT_MS);
+            if (mDozeScrimController.isPulsing()) {
+
+                // If we are waking the device up while we are pulsing the clock and the
+                // notifications would light up first, creating an unpleasant animation.
+                // Defer changing the screen brightness by forcing doze brightness on our window
+                // until the clock and the notifications are faded out.
+                mStatusBarWindowManager.setForceDozeBrightness(true);
+            }
+        }
+    }
+
+    @Override
+    public void onFingerprintAuthenticated(int userId) {
+        boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
+        mMode = calculateMode();
+        if (!wasDeviceInteractive) {
+            if (DEBUG_FP_WAKELOCK) {
+                Log.i(TAG, "fp wakelock: Authenticated, waking up...");
+            }
+            mPowerManager.wakeUp(SystemClock.uptimeMillis());
+        }
+        releaseFingerprintWakeLock();
+        switch (mMode) {
+            case MODE_DISMISS_BOUNCER:
+                mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
+                        false /* strongAuth */);
+                break;
+            case MODE_UNLOCK:
+            case MODE_SHOW_BOUNCER:
+                if (!wasDeviceInteractive) {
+                    mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+                }
+                mStatusBarKeyguardViewManager.animateCollapsePanels(
+                        FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
+                break;
+            case MODE_WAKE_AND_UNLOCK_PULSING:
+                mPhoneStatusBar.updateMediaMetaData(false /* metaDataChanged */);
+                // Fall through.
+            case MODE_WAKE_AND_UNLOCK:
+                mStatusBarWindowManager.setStatusBarFocusable(false);
+                mDozeScrimController.abortPulsing();
+                mKeyguardViewMediator.onWakeAndUnlocking();
+                mScrimController.setWakeAndUnlocking();
+                if (mPhoneStatusBar.getNavigationBarView() != null) {
+                    mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
+                }
+                break;
+            case MODE_ONLY_WAKE:
+            case MODE_NONE:
+                break;
+        }
+        if (mMode != MODE_WAKE_AND_UNLOCK_PULSING) {
+            mStatusBarWindowManager.setForceDozeBrightness(false);
+        }
+        mPhoneStatusBar.notifyFpAuthModeChanged();
+    }
+
+    public int getMode() {
+        return mMode;
+    }
+
+    private int calculateMode() {
+        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
+        if (!mUpdateMonitor.isDeviceInteractive()) {
+            if (!mStatusBarKeyguardViewManager.isShowing()) {
+                return MODE_ONLY_WAKE;
+            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
+                return MODE_WAKE_AND_UNLOCK_PULSING;
+            } else if (unlockingAllowed) {
+                return MODE_WAKE_AND_UNLOCK;
+            } else {
+                return MODE_SHOW_BOUNCER;
+            }
+        }
+        if (mStatusBarKeyguardViewManager.isShowing()) {
+            if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
+                return MODE_DISMISS_BOUNCER;
+            } else if (unlockingAllowed) {
+                return MODE_UNLOCK;
+            } else {
+                return MODE_SHOW_BOUNCER;
+            }
+        }
+        return MODE_NONE;
+    }
+
+    @Override
+    public void onFingerprintAuthFailed() {
+        cleanup();
+    }
+
+    @Override
+    public void onFingerprintError(int msgId, String errString) {
+        cleanup();
+    }
+
+    private void cleanup() {
+        mMode = MODE_NONE;
+        releaseFingerprintWakeLock();
+        mStatusBarWindowManager.setForceDozeBrightness(false);
+        mPhoneStatusBar.notifyFpAuthModeChanged();
+    }
+
+    public void startKeyguardFadingAway() {
+
+        // Disable brightness override when the ambient contents are fully invisible.
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mStatusBarWindowManager.setForceDozeBrightness(false);
+            }
+        }, PhoneStatusBar.FADE_KEYGUARD_DURATION_PULSING);
+    }
+
+    public void finishKeyguardFadingAway() {
+        mMode = MODE_NONE;
+        if (mPhoneStatusBar.getNavigationBarView() != null) {
+            mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
+        }
+        mPhoneStatusBar.notifyFpAuthModeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 10019f9..60ebfdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -86,9 +86,9 @@
         mContext = context;
         mCallback = callback;
         initIcons();
-        updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true);
-        updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true);
-        updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true);
+        updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
+        updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true, false);
+        updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
         initDimens();
     }
 
@@ -144,9 +144,7 @@
                 } else {
                     mTouchSlopExeeded = false;
                 }
-                mCallback.onSwipingStarted(targetView == mRightIcon);
-                mSwipingInProgress = true;
-                mTargetedView = targetView;
+                startSwiping(targetView);
                 mInitialTouchX = x;
                 mInitialTouchY = y;
                 mTranslationOnDown = mTranslation;
@@ -192,6 +190,12 @@
         return true;
     }
 
+    private void startSwiping(View targetView) {
+        mCallback.onSwipingStarted(targetView == mRightIcon);
+        mSwipingInProgress = true;
+        mTargetedView = targetView;
+    }
+
     private View getIconAtPosition(float x, float y) {
         if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
             return mLeftIcon;
@@ -324,7 +328,7 @@
         boolean velIsInWrongDirection = vel * mTranslation < 0;
         snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
         vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
-        fling(vel, snapBack || forceSnapBack);
+        fling(vel, snapBack || forceSnapBack, mTranslation < 0);
     }
 
     private boolean isBelowFalsingThreshold() {
@@ -336,9 +340,8 @@
         return (int) (mMinTranslationAmount * factor);
     }
 
-    private void fling(float vel, final boolean snapBack) {
-        float target = mTranslation < 0
-                ? -mCallback.getMaxTranslationDistance()
+    private void fling(float vel, final boolean snapBack, boolean right) {
+        float target = right ? -mCallback.getMaxTranslationDistance()
                 : mCallback.getMaxTranslationDistance();
         target = snapBack ? 0 : target;
 
@@ -352,8 +355,8 @@
         });
         animator.addListener(mFlingEndListener);
         if (!snapBack) {
-            startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable);
-            mCallback.onAnimationToSideStarted(mTranslation < 0, mTranslation, vel);
+            startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
+            mCallback.onAnimationToSideStarted(right, mTranslation, vel);
         } else {
             reset(true);
         }
@@ -364,8 +367,9 @@
         }
     }
 
-    private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
-        KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+    private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+            boolean right) {
+        KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
         targetView.finishAnimation(velocity, mAnimationEndRunnable);
     }
 
@@ -383,19 +387,20 @@
             fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
 
             boolean animateIcons = isReset && animateReset;
+            boolean forceNoCircleAnimation = isReset && !animateReset;
             float radius = getRadiusFromTranslation(absTranslation);
             boolean slowAnimation = isReset && isBelowFalsingThreshold();
             if (!isReset) {
                 updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
-                        false, false, false);
+                        false, false, false, false);
             } else {
                 updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
-                        animateIcons, slowAnimation, false);
+                        animateIcons, slowAnimation, false, forceNoCircleAnimation);
             }
             updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
-                    animateIcons, slowAnimation, false);
+                    animateIcons, slowAnimation, false, forceNoCircleAnimation);
             updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
-                    animateIcons, slowAnimation, false);
+                    animateIcons, slowAnimation, false, forceNoCircleAnimation);
 
             mTranslation = translation;
         }
@@ -431,16 +436,21 @@
 
     public void animateHideLeftRightIcon() {
         cancelAnimation();
-        updateIcon(mRightIcon, 0f, 0f, true, false, false);
-        updateIcon(mLeftIcon, 0f, 0f, true, false, false);
+        updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
+        updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
     }
 
     private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
-            boolean animate, boolean slowRadiusAnimation, boolean force) {
+                            boolean animate, boolean slowRadiusAnimation, boolean force,
+                            boolean forceNoCircleAnimation) {
         if (view.getVisibility() != View.VISIBLE && !force) {
             return;
         }
-        view.setCircleRadius(circleRadius, slowRadiusAnimation);
+        if (forceNoCircleAnimation) {
+            view.setCircleRadiusWithoutAnimation(circleRadius);
+        } else {
+            view.setCircleRadius(circleRadius, slowRadiusAnimation);
+        }
         updateIconAlpha(view, alpha, animate);
     }
 
@@ -503,8 +513,36 @@
         mMotionCancelled = true;
         if (mSwipingInProgress) {
             mCallback.onSwipingAborted();
+            mSwipingInProgress = false;
         }
-        mSwipingInProgress = false;
+    }
+
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public void launchAffordance(boolean animate, boolean left) {
+        if (mSwipingInProgress) {
+            // We don't want to mess with the state if the user is actually swiping already.
+            return;
+        }
+        KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
+        KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
+        startSwiping(targetView);
+        if (animate) {
+            fling(0, false, !left);
+            updateIcon(otherView, 0.0f, 0, true, false, true, false);
+            updateIcon(mCenterIcon, 0.0f, 0, true, false, true, false);
+        } else {
+            mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
+            mTranslation = left ? mCallback.getMaxTranslationDistance()
+                    : mCallback.getMaxTranslationDistance();
+            updateIcon(mCenterIcon, 0.0f, 0.0f, false, false, true, false);
+            updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
+            targetView.instantFinishAnimation();
+            mFlingEndListener.onAnimationEnd(null);
+            mAnimationEndRunnable.run();
+        }
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 1a2fa97..d30411a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -29,7 +29,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.hardware.fingerprint.FingerprintManager;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -81,12 +80,11 @@
     private static final Intent SECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
                     .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-    private static final Intent INSECURE_CAMERA_INTENT =
+    public static final Intent INSECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
     private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
-    private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
 
     private KeyguardAffordanceView mCameraImageView;
     private KeyguardAffordanceView mLeftAffordanceView;
@@ -528,7 +526,7 @@
         return mCameraPreview;
     }
 
-    public KeyguardAffordanceView getLockIcon() {
+    public LockIcon getLockIcon() {
         return mLockIcon;
     }
 
@@ -613,21 +611,6 @@
         }
     };
 
-    private final Runnable mTransientFpErrorClearRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mLockIcon.setTransientFpError(false);
-            mIndicationController.hideTransientIndication();
-        }
-    };
-
-    private final Runnable mHideTransientIndicationRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mIndicationController.hideTransientIndication();
-        }
-    };
-
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
         @Override
@@ -646,12 +629,18 @@
         }
 
         @Override
-        public void onKeyguardVisibilityChanged(boolean showing) {
-            mLockIcon.update();
+        public void onScreenTurnedOn() {
+            mLockIcon.setScreenOn(true);
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+        public void onScreenTurnedOff() {
+            mLockIcon.setScreenOn(false);
+        }
+
+        @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            mLockIcon.update();
         }
 
         @Override
@@ -660,28 +649,8 @@
         }
 
         @Override
-        public void onFingerprintHelp(int msgId, String helpString) {
-            if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) {
-                return;
-            }
-            mLockIcon.setTransientFpError(true);
-            mIndicationController.showTransientIndication(helpString,
-                    getResources().getColor(R.color.system_warning_color, null));
-            removeCallbacks(mTransientFpErrorClearRunnable);
-            postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT);
-        }
-
-        @Override
-        public void onFingerprintError(int msgId, String errString) {
-            if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()
-                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
-                return;
-            }
-            // TODO: Go to bouncer if this is "too many attempts" (lockout) error.
-            mIndicationController.showTransientIndication(errString,
-                    getResources().getColor(R.color.system_warning_color, null));
-            removeCallbacks(mHideTransientIndicationRunnable);
-            postDelayed(mHideTransientIndicationRunnable, 5000);
+        public void onStrongAuthStateChanged(int userId) {
+            mLockIcon.update();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index e9b2c61..893b352 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
-import android.view.Choreographer;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -27,6 +26,8 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardHostView;
 import com.android.keyguard.KeyguardSecurityView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.R;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
@@ -48,6 +49,13 @@
     private ViewGroup mRoot;
     private boolean mShowingSoon;
     private int mBouncerPromptReason;
+    private KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onStrongAuthStateChanged(int userId) {
+                    mBouncerPromptReason = mCallback.getBouncerPromptReason();
+                }
+            };
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
@@ -57,6 +65,7 @@
         mLockPatternUtils = lockPatternUtils;
         mContainer = container;
         mWindowManager = windowManager;
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
     }
 
     public void show(boolean resetSecuritySelection) {
@@ -103,6 +112,10 @@
         mKeyguardView.showPromptReason(reason);
     }
 
+    public void showMessage(String message, int color) {
+        mKeyguardView.showMessage(message, color);
+    }
+
     private void cancelShowRunnable() {
         DejankUtils.removeCallbacks(mShowRunnable);
         mShowingSoon = false;
@@ -244,8 +257,8 @@
         return mKeyguardView.interceptMediaKey(event);
     }
 
-    public void notifyKeyguardAuthenticated() {
+    public void notifyKeyguardAuthenticated(boolean strongAuth) {
         ensureView();
-        mKeyguardView.finish();
+        mKeyguardView.finish(strongAuth);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 06d2fca..463abfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -34,12 +34,6 @@
  */
 public class LockIcon extends KeyguardAffordanceView {
 
-    /**
-     * Delay animations a bit when the screen just turned on as a heuristic to start them after
-     * the screen has actually turned on.
-     */
-    private static final long ANIM_DELAY_AFTER_SCREEN_ON = 250;
-
     private static final int STATE_LOCKED = 0;
     private static final int STATE_LOCK_OPEN = 1;
     private static final int STATE_FACE_UNLOCK = 2;
@@ -50,6 +44,8 @@
     private boolean mLastDeviceInteractive;
     private boolean mTransientFpError;
     private boolean mDeviceInteractive;
+    private boolean mScreenOn;
+    private boolean mLastScreenOn;
     private final TrustDrawable mTrustDrawable;
     private final UnlockMethodCache mUnlockMethodCache;
     private AccessibilityController mAccessibilityController;
@@ -88,6 +84,11 @@
         update();
     }
 
+    public void setScreenOn(boolean screenOn) {
+        mScreenOn = screenOn;
+        update();
+    }
+
     public void update() {
         boolean visible = isShown()
                 && KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
@@ -96,20 +97,20 @@
         } else {
             mTrustDrawable.stop();
         }
-        if (!visible) {
-            return;
-        }
         // TODO: Real icon for facelock.
         int state = getState();
         boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
-        if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive) {
+        if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
+                || mScreenOn != mLastScreenOn) {
+            boolean isAnim = true;
             int iconRes = getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
-                    mDeviceInteractive);
+                    mDeviceInteractive, mLastScreenOn, mScreenOn);
             if (iconRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
                 anyFingerprintIcon = true;
             }
             if (iconRes == -1) {
-                iconRes = getIconForState(state);
+                iconRes = getIconForState(state, mScreenOn, mDeviceInteractive);
+                isAnim = false;
             }
             Drawable icon = mContext.getDrawable(iconRes);
             final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
@@ -135,23 +136,12 @@
                     : R.string.accessibility_unlock_button);
             setContentDescription(contentDescription);
             mHasFingerPrintIcon = anyFingerprintIcon;
-            if (animation != null) {
-
-                // If we play the draw on animation, delay it by one frame when the screen is
-                // actually turned on.
-                if (iconRes == R.drawable.lockscreen_fingerprint_draw_on_animation) {
-                    postOnAnimationDelayed(new Runnable() {
-                        @Override
-                        public void run() {
-                            animation.start();
-                        }
-                    }, ANIM_DELAY_AFTER_SCREEN_ON);
-                } else {
-                    animation.start();
-                }
+            if (animation != null && isAnim) {
+                animation.start();
             }
             mLastState = state;
             mLastDeviceInteractive = mDeviceInteractive;
+            mLastScreenOn = mScreenOn;
         }
 
         // Hide trust circle when fingerprint is running.
@@ -192,7 +182,7 @@
         mAccessibilityController = accessibilityController;
     }
 
-    private int getIconForState(int state) {
+    private int getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
         switch (state) {
             case STATE_LOCKED:
                 return R.drawable.ic_lock_24dp;
@@ -201,7 +191,11 @@
             case STATE_FACE_UNLOCK:
                 return com.android.internal.R.drawable.ic_account_circle;
             case STATE_FINGERPRINT:
-                return R.drawable.ic_fingerprint;
+                // If screen is off and device asleep, use the draw on animation so the first frame
+                // gets drawn.
+                return screenOn && deviceInteractive
+                        ? R.drawable.ic_fingerprint
+                        : R.drawable.lockscreen_fingerprint_draw_on_animation;
             case STATE_FINGERPRINT_ERROR:
                 return R.drawable.ic_fingerprint_error;
             default:
@@ -209,8 +203,9 @@
         }
     }
 
-    private int getAnimationResForTransition(int oldState, int newState, boolean oldScreenOn,
-            boolean screenOn) {
+    private int getAnimationResForTransition(int oldState, int newState,
+            boolean oldDeviceInteractive, boolean deviceInteractive,
+            boolean oldScreenOn, boolean screenOn) {
         if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
             return R.drawable.lockscreen_fingerprint_fp_to_error_state_animation;
         } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
@@ -218,7 +213,8 @@
         } else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN
                 && !mUnlockMethodCache.isTrusted()) {
             return R.drawable.lockscreen_fingerprint_draw_off_animation;
-        } else if (newState == STATE_FINGERPRINT && !oldScreenOn && screenOn) {
+        } else if (newState == STATE_FINGERPRINT && (!oldScreenOn && screenOn && deviceInteractive
+                || screenOn && !oldDeviceInteractive && deviceInteractive)) {
             return R.drawable.lockscreen_fingerprint_draw_on_animation;
         } else {
             return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 059ecee..cc31476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -91,7 +91,8 @@
 
     private OnVerticalChangedListener mOnVerticalChangedListener;
     private boolean mIsLayoutRtl;
-    private boolean mLayoutTransitionsEnabled;
+    private boolean mLayoutTransitionsEnabled = true;
+    private boolean mWakeAndUnlocking;
 
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
@@ -361,13 +362,19 @@
         }
     }
 
-    public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
-        setUseFadingAnimations(wakeAndUnlocking);
-        setLayoutTransitionsEnabled(!wakeAndUnlocking);
+    public void setLayoutTransitionsEnabled(boolean enabled) {
+        mLayoutTransitionsEnabled = enabled;
+        updateLayoutTransitionsEnabled();
     }
 
-    private void setLayoutTransitionsEnabled(boolean enabled) {
-        mLayoutTransitionsEnabled = enabled;
+    public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
+        setUseFadingAnimations(wakeAndUnlocking);
+        mWakeAndUnlocking = wakeAndUnlocking;
+        updateLayoutTransitionsEnabled();
+    }
+
+    private void updateLayoutTransitionsEnabled() {
+        boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
         ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
         LayoutTransition lt = navButtons.getLayoutTransition();
         if (lt != null) {
@@ -459,7 +466,7 @@
         }
         mCurrentView = mRotatedViews[rot];
         mCurrentView.setVisibility(View.VISIBLE);
-        setLayoutTransitionsEnabled(mLayoutTransitionsEnabled);
+        updateLayoutTransitionsEnabled();
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
 
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 373abe5..5557f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -203,6 +203,7 @@
     private int mLastOrientation = -1;
     private boolean mClosingWithAlphaFadeOut;
     private boolean mHeadsUpAnimatingAway;
+    private boolean mLaunchingAffordance;
 
     private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
         @Override
@@ -488,7 +489,9 @@
         mIsLaunchTransitionFinished = false;
         mBlockTouches = false;
         mUnlockIconActive = false;
-        mAfforanceHelper.reset(true);
+        if (!mLaunchingAffordance) {
+            mAfforanceHelper.reset(false);
+        }
         closeQs();
         mStatusBar.dismissPopups();
         mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
@@ -918,7 +921,7 @@
     }
 
     private int getFalsingThreshold() {
-        float factor = mStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+        float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
         return (int) (mQsFalsingThreshold * factor);
     }
 
@@ -2075,7 +2078,7 @@
 
     @Override
     public float getAffordanceFalsingFactor() {
-        return mStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+        return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
     }
 
     @Override
@@ -2388,4 +2391,41 @@
     protected boolean isPanelVisibleBecauseOfHeadsUp() {
         return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return !mDozing;
+    }
+
+    public void launchCamera(boolean animate) {
+        // If we are launching it when we are occluded already we don't want it to animate,
+        // nor setting these flags, since the occluded state doesn't change anymore, hence it's
+        // never reset.
+        if (!isFullyCollapsed()) {
+            mLaunchingAffordance = true;
+            setLaunchingAffordance(true);
+        } else {
+            animate = false;
+        }
+        mAfforanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+    }
+
+    public void onAffordanceLaunchEnded() {
+        mLaunchingAffordance = false;
+        setLaunchingAffordance(false);
+    }
+
+    /**
+     * Set whether we are currently launching an affordance. This is currently only set when
+     * launched via a camera gesture.
+     */
+    private void setLaunchingAffordance(boolean launchingAffordance) {
+        getLeftIcon().setLaunchingAffordance(launchingAffordance);
+        getRightIcon().setLaunchingAffordance(launchingAffordance);
+        getCenterIcon().setLaunchingAffordance(launchingAffordance);
+    }
+
+    public boolean canCameraGestureBeLaunched() {
+        return !mAfforanceHelper.isSwipingInProgress();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 8b25e08..c6743e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -382,7 +382,7 @@
                     || forceCancel;
             DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
                     mStatusBar.isFalsingThresholdNeeded(),
-                    mStatusBar.isScreenOnComingFromTouch());
+                    mStatusBar.isWakeUpComingFromTouch());
                     // Log collapse gesture if on lock screen.
                     if (!expand && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
                         float displayDensity = mStatusBar.getDisplayDensity();
@@ -411,7 +411,7 @@
     }
 
     private int getFalsingThreshold() {
-        float factor = mStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+        float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
         return (int) (mUnlockFalsingThreshold * factor);
     }
 
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 18cf95b..8063bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -66,6 +66,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -99,6 +100,7 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DemoMode;
@@ -153,6 +155,7 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.statusbar.stack.StackViewState;
 import com.android.systemui.volume.VolumeComponent;
 
@@ -230,6 +233,7 @@
 
     public static final int FADE_KEYGUARD_START_DELAY = 100;
     public static final int FADE_KEYGUARD_DURATION = 300;
+    public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
 
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -270,6 +274,7 @@
     KeyguardMonitor mKeyguardMonitor;
     BrightnessMirrorController mBrightnessMirrorController;
     AccessibilityController mAccessibilityController;
+    FingerprintUnlockController mFingerprintUnlockController;
 
     int mNaturalBarHeight = -1;
 
@@ -282,8 +287,8 @@
     private StatusBarWindowManager mStatusBarWindowManager;
     private UnlockMethodCache mUnlockMethodCache;
     private DozeServiceHost mDozeServiceHost;
-    private boolean mScreenOnComingFromTouch;
-    private PointF mScreenOnTouchLocation;
+    private boolean mWakeUpComingFromTouch;
+    private PointF mWakeUpTouchLocation;
 
     int mPixelFormat;
     Object mQueueLock = new Object();
@@ -478,6 +483,9 @@
     private Runnable mLaunchTransitionEndRunnable;
     private boolean mLaunchTransitionFadingAway;
     private ExpandableNotificationRow mDraggedDownRow;
+    private boolean mLaunchCameraOnScreenTurningOn;
+    private PowerManager.WakeLock mGestureWakeLock;
+    private Vibrator mVibrator;
 
     // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
     private int mLastLoggedStateFingerprint;
@@ -621,6 +629,7 @@
         startKeyguard();
 
         mDozeServiceHost = new DozeServiceHost();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost);
         putComponent(DozeHost.class, mDozeServiceHost);
         putComponent(PhoneStatusBar.class, this);
 
@@ -780,7 +789,8 @@
         mKeyguardBottomArea.setAssistManager(mAssistManager);
         mKeyguardIndicationController = new KeyguardIndicationController(mContext,
                 (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
-                        R.id.keyguard_indication_text));
+                        R.id.keyguard_indication_text),
+                mKeyguardBottomArea.getLockIcon());
         mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
 
         // set the inital view visibility
@@ -897,7 +907,9 @@
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
-
+        mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+                "GestureWakeLock");
+        mVibrator = mContext.getSystemService(Vibrator.class);
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -1009,8 +1021,15 @@
 
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
+        mFingerprintUnlockController = new FingerprintUnlockController(mContext,
+                mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
+                mScrimController, this);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
-                mStatusBarWindow, mStatusBarWindowManager, mScrimController);
+                mStatusBarWindow, mStatusBarWindowManager, mScrimController,
+                mFingerprintUnlockController);
+        mKeyguardIndicationController.setStatusBarKeyguardViewManager(
+                mStatusBarKeyguardViewManager);
+        mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
     }
 
@@ -1681,7 +1700,9 @@
         final boolean hasArtwork = artworkBitmap != null;
 
         if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
-                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
+                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+                && mFingerprintUnlockController.getMode()
+                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
             // time to show some art!
             if (mBackdrop.getVisibility() != View.VISIBLE) {
                 mBackdrop.setVisibility(View.VISIBLE);
@@ -1736,31 +1757,40 @@
                 if (DEBUG_MEDIA) {
                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
                 }
-                mBackdrop.animate()
-                        // Never let the alpha become zero - otherwise the RenderNode
-                        // won't draw anything and uninitialized memory will show through
-                        // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in libhwui.
-                        .alpha(0.002f)
-                        .setInterpolator(mBackdropInterpolator)
-                        .setDuration(300)
-                        .setStartDelay(0)
-                        .withEndAction(new Runnable() {
-                            @Override
-                            public void run() {
-                                mBackdrop.setVisibility(View.GONE);
-                                mBackdropFront.animate().cancel();
-                                mBackdropBack.animate().cancel();
-                                mHandler.post(mHideBackdropFront);
-                            }
-                        });
-                if (mKeyguardFadingAway) {
-                    mBackdrop.animate()
+                if (mFingerprintUnlockController.getMode()
+                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
 
-                            // Make it disappear faster, as the focus should be on the activity behind.
-                            .setDuration(mKeyguardFadingAwayDuration / 2)
-                            .setStartDelay(mKeyguardFadingAwayDelay)
-                            .setInterpolator(mLinearInterpolator)
-                            .start();
+                    // We are unlocking directly - no animation!
+                    mBackdrop.setVisibility(View.GONE);
+                } else {
+                    mBackdrop.animate()
+                            // Never let the alpha become zero - otherwise the RenderNode
+                            // won't draw anything and uninitialized memory will show through
+                            // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
+                            // libhwui.
+                            .alpha(0.002f)
+                            .setInterpolator(mBackdropInterpolator)
+                            .setDuration(300)
+                            .setStartDelay(0)
+                            .withEndAction(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mBackdrop.setVisibility(View.GONE);
+                                    mBackdropFront.animate().cancel();
+                                    mBackdropBack.animate().cancel();
+                                    mHandler.post(mHideBackdropFront);
+                                }
+                            });
+                    if (mKeyguardFadingAway) {
+                        mBackdrop.animate()
+
+                                // Make it disappear faster, as the focus should be on the activity
+                                // behind.
+                                .setDuration(mKeyguardFadingAwayDuration / 2)
+                                .setStartDelay(mKeyguardFadingAwayDelay)
+                                .setInterpolator(mLinearInterpolator)
+                                .start();
+                    }
                 }
             }
         }
@@ -1910,8 +1940,8 @@
         return mNotificationPanel.isQsExpanded();
     }
 
-    public boolean isScreenOnComingFromTouch() {
-        return mScreenOnComingFromTouch;
+    public boolean isWakeUpComingFromTouch() {
+        return mWakeUpComingFromTouch;
     }
 
     public boolean isFalsingThresholdNeeded() {
@@ -2423,8 +2453,12 @@
                         || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
                 boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
                 boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
-
-                mIconController.setIconsDark(allowLight && light);
+                boolean animate = mFingerprintUnlockController == null
+                        || (mFingerprintUnlockController.getMode()
+                                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                        && mFingerprintUnlockController.getMode()
+                                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+                mIconController.setIconsDark(allowLight && light, animate);
             }
             // restore the recents bit
             if (wasRecentsVisible) {
@@ -2471,7 +2505,7 @@
     private void checkBarMode(int mode, int windowState, BarTransitions transitions,
             boolean noAnimation) {
         final boolean powerSave = mBatteryController.isPowerSave();
-        final boolean anim = !noAnimation && (mScreenOn == null || mScreenOn)
+        final boolean anim = !noAnimation && mDeviceInteractive
                 && windowState != WINDOW_STATE_HIDDEN && !powerSave;
         if (powerSave && getBarState() == StatusBarState.SHADE) {
             mode = MODE_WARNING;
@@ -2879,14 +2913,12 @@
                 }
             }
             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                mScreenOn = false;
                 notifyNavigationBarScreenOn(false);
                 notifyHeadsUpScreenOff();
                 finishBarAnimations();
                 resetUserExpandedStates();
             }
             else if (Intent.ACTION_SCREEN_ON.equals(action)) {
-                mScreenOn = true;
                 notifyNavigationBarScreenOn(true);
             }
         }
@@ -3340,7 +3372,7 @@
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         setBarState(StatusBarState.KEYGUARD);
         updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
-        if (!mScreenOnFromKeyguard) {
+        if (!mDeviceInteractive) {
 
             // If the screen is off already, we need to disable touch events because these might
             // collapse the panel after we expanded it, and thus we would end up with a blank
@@ -3359,6 +3391,8 @@
 
     private void onLaunchTransitionFadingEnded() {
         mNotificationPanel.setAlpha(1.0f);
+        mNotificationPanel.onAffordanceLaunchEnded();
+        releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
         mLaunchTransitionFadingAway = false;
         mScrimController.forceHideScrims(false /* hide */);
@@ -3422,6 +3456,19 @@
     }
 
     /**
+     * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
+     * fading.
+     */
+    public void fadeKeyguardWhilePulsing() {
+        mNotificationPanel.animate()
+                .alpha(0f)
+                .setStartDelay(0)
+                .setDuration(FADE_KEYGUARD_DURATION_PULSING)
+                .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR)
+                .start();
+    }
+
+    /**
      * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
      * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
      * because the launched app crashed or something else went wrong.
@@ -3433,6 +3480,8 @@
 
     private void onLaunchTransitionTimeout() {
         Log.w(TAG, "Launch transition: Timeout!");
+        mNotificationPanel.onAffordanceLaunchEnded();
+        releaseGestureWakeLock();
         mNotificationPanel.resetViews();
     }
 
@@ -3455,11 +3504,24 @@
         setBarState(StatusBarState.SHADE);
         if (mLeaveOpenOnKeyguardHide) {
             mLeaveOpenOnKeyguardHide = false;
-            mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
+            long delay = calculateGoingToFullShadeDelay();
+            mNotificationPanel.animateToFullShade(delay);
             if (mDraggedDownRow != null) {
                 mDraggedDownRow.setUserLocked(false);
                 mDraggedDownRow = null;
             }
+
+            // Disable layout transitions in navbar for this transition because the load is just
+            // too heavy for the CPU and GPU on any device.
+            if (mNavigationBarView != null) {
+                mNavigationBarView.setLayoutTransitionsEnabled(false);
+                mNavigationBarView.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mNavigationBarView.setLayoutTransitionsEnabled(true);
+                    }
+                }, delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
+            }
         } else {
             instantCollapseNotificationPanel();
         }
@@ -3471,9 +3533,18 @@
             mQSPanel.refreshAllTiles();
         }
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+        releaseGestureWakeLock();
+        mNotificationPanel.onAffordanceLaunchEnded();
+        mNotificationPanel.setAlpha(1f);
         return staying;
     }
 
+    private void releaseGestureWakeLock() {
+        if (mGestureWakeLock.isHeld()) {
+            mGestureWakeLock.release();
+        }
+    }
+
     public long calculateGoingToFullShadeDelay() {
         return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
     }
@@ -3560,9 +3631,14 @@
     private void updateDozingState() {
         boolean animate = !mDozing && mDozeScrimController.isPulsing();
         mNotificationPanel.setDozing(mDozing, animate);
-        mStackScroller.setDark(mDozing, animate, mScreenOnTouchLocation);
+        mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
         mScrimController.setDozing(mDozing);
-        mDozeScrimController.setDozing(mDozing, animate);
+
+        // Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
+        // for pulsing so the Keyguard fade-out animation scrim can take over.
+        mDozeScrimController.setDozing(mDozing &&
+                mFingerprintUnlockController.getMode()
+                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
     }
 
     public void updateStackScrollerState(boolean goingToFullShade) {
@@ -3593,6 +3669,11 @@
         return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
     }
 
+    public void endAffordanceLaunch() {
+        releaseGestureWakeLock();
+        mNotificationPanel.onAffordanceLaunchEnded();
+    }
+
     public boolean onBackPressed() {
         if (mStatusBarKeyguardViewManager.onBackPressed()) {
             return true;
@@ -3613,7 +3694,7 @@
     }
 
     public boolean onSpacePressed() {
-        if (mScreenOn != null && mScreenOn
+        if (mDeviceInteractive
                 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
             animateCollapsePanels(
                     CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
@@ -3823,16 +3904,19 @@
         disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
     }
 
-    public void onScreenTurnedOff() {
-        mScreenOnFromKeyguard = false;
-        mScreenOnComingFromTouch = false;
-        mScreenOnTouchLocation = null;
+    public void onFinishedGoingToSleep() {
+        mNotificationPanel.onAffordanceLaunchEnded();
+        releaseGestureWakeLock();
+        mLaunchCameraOnScreenTurningOn = false;
+        mDeviceInteractive = false;
+        mWakeUpComingFromTouch = false;
+        mWakeUpTouchLocation = null;
         mStackScroller.setAnimationsEnabled(false);
         updateVisibleToUser();
     }
 
-    public void onScreenTurnedOn() {
-        mScreenOnFromKeyguard = true;
+    public void onStartedWakingUp() {
+        mDeviceInteractive = true;
         mStackScroller.setAnimationsEnabled(true);
         mNotificationPanel.setTouchDisabled(false);
         updateVisibleToUser();
@@ -3840,6 +3924,18 @@
 
     public void onScreenTurningOn() {
         mNotificationPanel.onScreenTurningOn();
+        if (mLaunchCameraOnScreenTurningOn) {
+            mNotificationPanel.launchCamera(false);
+            mLaunchCameraOnScreenTurningOn = false;
+        }
+    }
+
+    private void vibrateForCameraGesture() {
+        mVibrator.vibrate(1000L);
+    }
+
+    public void onScreenTurnedOn() {
+        mDozeScrimController.onScreenTurnedOn();
     }
 
     /**
@@ -3956,8 +4052,8 @@
         if (mDozing && mDozeScrimController.isPulsing()) {
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
             pm.wakeUp(time, "com.android.systemui:NODOZE");
-            mScreenOnComingFromTouch = true;
-            mScreenOnTouchLocation = new PointF(event.getX(), event.getY());
+            mWakeUpComingFromTouch = true;
+            mWakeUpTouchLocation = new PointF(event.getX(), event.getY());
             mNotificationPanel.setTouchDisabled(false);
             mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
         }
@@ -3982,8 +4078,9 @@
     public void appTransitionStarting(long startTime, long duration) {
 
         // Use own timings when Keyguard is going away, see keyguardGoingAway and
-        // setKeyguardFadingAway
-        if (!mKeyguardFadingAway) {
+        // setKeyguardFadingAway. When duration is 0, skip this one because no animation is really
+        // playing.
+        if (!mKeyguardFadingAway && duration > 0) {
             mIconController.appTransitionStarting(startTime, duration);
         }
         if (mIconPolicy != null) {
@@ -3991,8 +4088,48 @@
         }
     }
 
+    @Override
+    public void onCameraLaunchGestureDetected() {
+        if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+            return;
+        }
+        if (!mDeviceInteractive) {
+            PowerManager pm = mContext.getSystemService(PowerManager.class);
+            pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
+            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+        }
+        vibrateForCameraGesture();
+        if (!mStatusBarKeyguardViewManager.isShowing()) {
+            startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
+                    true /* dismissShade */);
+        } else {
+            if (!mDeviceInteractive) {
+                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+                // comes on.
+                mScrimController.dontAnimateBouncerChangesUntilNextFrame();
+                mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+            }
+            if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+            } else {
+                // We need to defer the camera launch until the screen comes on, since otherwise
+                // we will dismiss us too early since we are waiting on an activity to be drawn and
+                // incorrectly get notified because of the screen on event (which resumes and pauses
+                // some activities)
+                mLaunchCameraOnScreenTurningOn = true;
+            }
+        }
+    }
+
+    public void notifyFpAuthModeChanged() {
+        updateDozing();
+    }
+
     private void updateDozing() {
-        mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD;
+        // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
+        mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
+                || mFingerprintUnlockController.getMode()
+                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
         updateDozingState();
     }
 
@@ -4022,7 +4159,7 @@
         }
     }
 
-    private final class DozeServiceHost implements DozeHost {
+    private final class DozeServiceHost extends KeyguardUpdateMonitorCallback implements DozeHost  {
         // Amount of time to allow to update the time shown on the screen before releasing
         // the wakelock.  This timeout is design to compensate for the fact that we don't
         // currently have a way to know when time display contents have actually been
@@ -4096,6 +4233,12 @@
         }
 
         @Override
+        public boolean isPulsingBlocked() {
+            return mFingerprintUnlockController.getMode()
+                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+        }
+
+        @Override
         public boolean isNotificationLightOn() {
             return mNotificationLightOn;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1a35500..b9e9292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -22,7 +22,6 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.animation.DecelerateInterpolator;
@@ -44,12 +43,15 @@
 public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
         HeadsUpManager.OnHeadsUpChangedListener {
     public static final long ANIMATION_DURATION = 220;
+    public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
+            = new PathInterpolator(0f, 0, 0.7f, 1f);
 
     private static final float SCRIM_BEHIND_ALPHA = 0.62f;
     private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
     private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
     private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
     private static final int TAG_KEY_ANIM = R.id.scrim;
+    private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
     private static final int TAG_HUN_START_ALPHA = R.id.hun_scrim_alpha_start;
     private static final int TAG_HUN_END_ALPHA = R.id.hun_scrim_alpha_end;
 
@@ -71,9 +73,7 @@
     private long mDurationOverride = -1;
     private long mAnimationDelay;
     private Runnable mOnAnimationFinished;
-    private boolean mAnimationStarted;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
-    private final Interpolator mKeyguardFadeOutInterpolator = new PathInterpolator(0f, 0, 0.7f, 1f);
     private BackDropView mBackDropView;
     private boolean mScrimSrcEnabled;
     private boolean mDozing;
@@ -86,6 +86,8 @@
     private float mTopHeadsUpDragAmount;
     private View mDraggedHeadsUpView;
     private boolean mForceHideScrims;
+    private boolean mSkipFirstFrame;
+    private boolean mDontAnimateBouncerChanges;
 
     public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
             boolean scrimSrcEnabled) {
@@ -124,7 +126,7 @@
 
     public void setBouncerShowing(boolean showing) {
         mBouncerShowing = showing;
-        mAnimateChange = !mExpanding;
+        mAnimateChange = !mExpanding && !mDontAnimateBouncerChanges;
         scheduleUpdate();
     }
 
@@ -133,19 +135,25 @@
         scheduleUpdate();
     }
 
-    public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) {
+    public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished,
+            boolean skipFirstFrame) {
         mWakeAndUnlocking = false;
         mAnimateKeyguardFadingOut = true;
         mDurationOverride = duration;
         mAnimationDelay = delay;
         mAnimateChange = true;
+        mSkipFirstFrame = skipFirstFrame;
         mOnAnimationFinished = onAnimationFinished;
         scheduleUpdate();
+
+        // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
+        // the changes we just scheduled.
+        onPreDraw();
     }
 
     public void abortKeyguardFadingOut() {
         if (mAnimateKeyguardFadingOut) {
-            endAnimateKeyguardFadingOut();
+            endAnimateKeyguardFadingOut(true /* force */);
         }
     }
 
@@ -198,8 +206,13 @@
 
             // During wake and unlock, we first hide everything behind a black scrim, which then
             // gets faded out from animateKeyguardFadingOut.
-            setScrimInFrontColor(1f);
-            setScrimBehindColor(0f);
+            if (mDozing) {
+                setScrimInFrontColor(0f);
+                setScrimBehindColor(1f);
+            } else {
+                setScrimInFrontColor(1f);
+                setScrimBehindColor(0f);
+            }
         } else if (!mKeyguardShowing && !mBouncerShowing) {
             updateScrimNormal();
             setScrimInFrontColor(0);
@@ -258,10 +271,14 @@
     }
 
     private void setScrimColor(View scrim, float alpha) {
-        Object runningAnim = scrim.getTag(TAG_KEY_ANIM);
-        if (runningAnim instanceof ValueAnimator) {
-            ((ValueAnimator) runningAnim).cancel();
-            scrim.setTag(TAG_KEY_ANIM, null);
+        ValueAnimator runningAnim = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM);
+        Float target = (Float) scrim.getTag(TAG_KEY_ANIM_TARGET);
+        if (runningAnim != null && target != null) {
+            if (alpha != target) {
+                runningAnim.cancel();
+            } else {
+                return;
+            }
         }
         if (mAnimateChange) {
             startScrimAnimation(scrim, alpha);
@@ -325,39 +342,51 @@
                     mOnAnimationFinished = null;
                 }
                 scrim.setTag(TAG_KEY_ANIM, null);
+                scrim.setTag(TAG_KEY_ANIM_TARGET, null);
             }
         });
         anim.start();
+        if (mSkipFirstFrame) {
+            anim.setCurrentPlayTime(16);
+        }
         scrim.setTag(TAG_KEY_ANIM, anim);
-        mAnimationStarted = true;
+        scrim.setTag(TAG_KEY_ANIM_TARGET, target);
     }
 
     private Interpolator getInterpolator() {
-        return mAnimateKeyguardFadingOut ? mKeyguardFadeOutInterpolator : mInterpolator;
+        return mAnimateKeyguardFadingOut ? KEYGUARD_FADE_OUT_INTERPOLATOR : mInterpolator;
     }
 
     @Override
     public boolean onPreDraw() {
         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
         mUpdatePending = false;
+        if (mDontAnimateBouncerChanges) {
+            mDontAnimateBouncerChanges = false;
+        }
         updateScrims();
         mDurationOverride = -1;
         mAnimationDelay = 0;
+        mSkipFirstFrame = false;
 
         // Make sure that we always call the listener even if we didn't start an animation.
-        endAnimateKeyguardFadingOut();
-        mAnimationStarted = false;
+        endAnimateKeyguardFadingOut(false /* force */);
         return true;
     }
 
-    private void endAnimateKeyguardFadingOut() {
+    private void endAnimateKeyguardFadingOut(boolean force) {
         mAnimateKeyguardFadingOut = false;
-        if (!mAnimationStarted && mOnAnimationFinished != null) {
+        if ((force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind)))
+                && mOnAnimationFinished != null) {
             mOnAnimationFinished.run();
             mOnAnimationFinished = null;
         }
     }
 
+    private boolean isAnimating(View scrim) {
+        return scrim.getTag(TAG_KEY_ANIM) != null;
+    }
+
     public void setBackDropView(BackDropView backDropView) {
         mBackDropView = backDropView;
         mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {
@@ -471,4 +500,8 @@
         mAnimateChange = false;
         scheduleUpdate();
     }
+
+    public void dontAnimateBouncerChangesUntilNextFrame() {
+        mDontAnimateBouncerChanges = true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index a1e9ece..18db5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -19,6 +19,7 @@
 import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.drawable.RippleDrawable;
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 7ee47df..e9c4e49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.util.TypedValue;
@@ -184,6 +185,12 @@
             }
         });
         requestCaptureValues();
+
+        // RenderThread is doing more harm than good when touching the header (to expand quick
+        // settings), so disable it for this view
+        ((RippleDrawable) getBackground()).setForceSoftware(true);
+        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+        ((RippleDrawable) mSystemIconsSuperContainer.getBackground()).setForceSoftware(true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 067e50e..5de1c13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -335,8 +335,10 @@
         }
     }
 
-    public void setIconsDark(boolean dark) {
-        if (mTransitionPending) {
+    public void setIconsDark(boolean dark, boolean animate) {
+        if (!animate) {
+            setIconTintInternal(dark ? 1.0f : 0.0f);
+        } else if (mTransitionPending) {
             deferIconTintChange(dark ? 1.0f : 0.0f);
         } else if (mTransitionDeferring) {
             animateIconTint(dark ? 1.0f : 0.0f,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7dd3e7c..d0604c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -48,6 +48,8 @@
     // with the appear animations of the PIN/pattern/password views.
     private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
 
+    private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
+
     private static String TAG = "StatusBarKeyguardViewManager";
 
     private final Context mContext;
@@ -56,6 +58,7 @@
     private ViewMediatorCallback mViewMediatorCallback;
     private PhoneStatusBar mPhoneStatusBar;
     private ScrimController mScrimController;
+    private FingerprintUnlockController mFingerprintUnlockController;
 
     private ViewGroup mContainer;
     private StatusBarWindowManager mStatusBarWindowManager;
@@ -71,10 +74,8 @@
     private boolean mLastOccluded;
     private boolean mLastBouncerShowing;
     private boolean mLastBouncerDismissible;
-    private boolean mLastDeferScrimFadeOut;
     private OnDismissAction mAfterKeyguardGoneAction;
     private boolean mDeviceWillWakeUp;
-    private boolean mWakeAndUnlocking;
     private boolean mDeferScrimFadeOut;
 
     public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
@@ -86,11 +87,13 @@
 
     public void registerStatusBar(PhoneStatusBar phoneStatusBar,
             ViewGroup container, StatusBarWindowManager statusBarWindowManager,
-            ScrimController scrimController) {
+            ScrimController scrimController,
+            FingerprintUnlockController fingerprintUnlockController) {
         mPhoneStatusBar = phoneStatusBar;
         mContainer = container;
         mStatusBarWindowManager = statusBarWindowManager;
         mScrimController = scrimController;
+        mFingerprintUnlockController = fingerprintUnlockController;
         mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
                 mStatusBarWindowManager, container);
     }
@@ -162,28 +165,33 @@
 
     public void onFinishedGoingToSleep() {
         mDeviceInteractive = false;
-        mPhoneStatusBar.onScreenTurnedOff();
+        mPhoneStatusBar.onFinishedGoingToSleep();
         mBouncer.onScreenTurnedOff();
     }
 
     public void onStartedWakingUp() {
         mDeviceInteractive = true;
         mDeviceWillWakeUp = false;
-        mPhoneStatusBar.onScreenTurnedOn();
+        mPhoneStatusBar.onStartedWakingUp();
     }
 
     public void onScreenTurningOn() {
         mPhoneStatusBar.onScreenTurningOn();
     }
 
+    public boolean isScreenTurnedOn() {
+        return mScreenTurnedOn;
+    }
+
     public void onScreenTurnedOn() {
         mScreenTurnedOn = true;
-        mWakeAndUnlocking = false;
         if (mDeferScrimFadeOut) {
             mDeferScrimFadeOut = false;
-            animateScrimControllerKeyguardFadingOut(0, 200);
+            animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
+                    true /* skipFirstFrame */);
             updateStates();
         }
+        mPhoneStatusBar.onScreenTurnedOn();
     }
 
     public void onScreenTurnedOff() {
@@ -260,7 +268,8 @@
                     updateStates();
                     mScrimController.animateKeyguardFadingOut(
                             PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
-                            PhoneStatusBar.FADE_KEYGUARD_DURATION, null);
+                            PhoneStatusBar.FADE_KEYGUARD_DURATION, null,
+                            false /* skipFirstFrame */);
                 }
             }, new Runnable() {
                 @Override
@@ -272,18 +281,43 @@
                 }
             });
         } else {
-            mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
-            boolean staying = mPhoneStatusBar.hideKeyguard();
-            if (!staying) {
+            if (mFingerprintUnlockController.getMode()
+                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+                mFingerprintUnlockController.startKeyguardFadingAway();
+                mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240);
                 mStatusBarWindowManager.setKeyguardFadingAway(true);
-                if (mWakeAndUnlocking && !mScreenTurnedOn) {
-                    mDeferScrimFadeOut = true;
-                } else {
-                    animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration);
-                }
+                mPhoneStatusBar.fadeKeyguardWhilePulsing();
+                animateScrimControllerKeyguardFadingOut(0, 240, new Runnable() {
+                    @Override
+                    public void run() {
+                        mPhoneStatusBar.hideKeyguard();
+                    }
+                }, false /* skipFirstFrame */);
             } else {
-                mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
-                mPhoneStatusBar.finishKeyguardFadingAway();
+                mFingerprintUnlockController.startKeyguardFadingAway();
+                mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
+                boolean staying = mPhoneStatusBar.hideKeyguard();
+                if (!staying) {
+                    mStatusBarWindowManager.setKeyguardFadingAway(true);
+                    if (mFingerprintUnlockController.getMode()
+                            == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+                        if (!mScreenTurnedOn) {
+                            mDeferScrimFadeOut = true;
+                        } else {
+
+                            // Screen is already on, don't defer with fading out.
+                            animateScrimControllerKeyguardFadingOut(0,
+                                    WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
+                                    true /* skipFirstFrame */);
+                        }
+                    } else {
+                        animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
+                                false /* skipFirstFrame */);
+                    }
+                } else {
+                    mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
+                    mPhoneStatusBar.finishKeyguardFadingAway();
+                }
             }
             mStatusBarWindowManager.setKeyguardShowing(false);
             mBouncer.hide(true /* destroyView */);
@@ -291,24 +325,31 @@
             executeAfterKeyguardGoneAction();
             updateStates();
         }
-
     }
 
-    private void animateScrimControllerKeyguardFadingOut(long delay, long duration) {
+    private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
+            boolean skipFirstFrame) {
+        animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
+                skipFirstFrame);
+    }
+
+    private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
+            final Runnable endRunnable, boolean skipFirstFrame) {
         Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0);
         mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() {
             @Override
             public void run() {
+                if (endRunnable != null) {
+                    endRunnable.run();
+                }
                 mStatusBarWindowManager.setKeyguardFadingAway(false);
                 mPhoneStatusBar.finishKeyguardFadingAway();
-                if (mPhoneStatusBar.getNavigationBarView() != null) {
-                    mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
-                }
+                mFingerprintUnlockController.finishKeyguardFadingAway();
                 WindowManagerGlobal.getInstance().trimMemory(
                         ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
             }
-        });
+        }, skipFirstFrame);
     }
 
     private void executeAfterKeyguardGoneAction() {
@@ -348,6 +389,7 @@
      */
     public boolean onBackPressed() {
         if (mBouncer.isShowing()) {
+            mPhoneStatusBar.endAffordanceLaunch();
             reset();
             return true;
         }
@@ -382,7 +424,6 @@
         boolean occluded = mOccluded;
         boolean bouncerShowing = mBouncer.isShowing();
         boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
-        boolean deferScrimFadeOut = mDeferScrimFadeOut;
 
         if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing)
                 || mFirstUpdate) {
@@ -393,16 +434,18 @@
             }
         }
 
-        // Hide navigation bar on Keyguard but not on bouncer and also if we are deferring a scrim
-        // fade out, i.e. we are waiting for the screen to have turned on.
-        boolean navBarVisible = !deferScrimFadeOut && (!(showing && !occluded) || bouncerShowing);
-        boolean lastNavBarVisible = !mLastDeferScrimFadeOut && (!(mLastShowing && !mLastOccluded)
-                || mLastBouncerShowing);
+        boolean navBarVisible = (!(showing && !occluded) || bouncerShowing);
+        boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing);
         if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
             if (mPhoneStatusBar.getNavigationBarView() != null) {
                 if (navBarVisible) {
-                    mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
-                            getNavBarShowDelay());
+                    long delay = getNavBarShowDelay();
+                    if (delay == 0) {
+                        mMakeNavigationBarVisibleRunnable.run();
+                    } else {
+                        mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
+                                delay);
+                    }
                 } else {
                     mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
                     mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
@@ -427,7 +470,6 @@
         mFirstUpdate = false;
         mLastShowing = showing;
         mLastOccluded = occluded;
-        mLastDeferScrimFadeOut = deferScrimFadeOut;
         mLastBouncerShowing = bouncerShowing;
         mLastBouncerDismissible = bouncerDismissible;
 
@@ -484,15 +526,11 @@
      * Notifies that the user has authenticated by other means than using the bouncer, for example,
      * fingerprint.
      */
-    public void notifyKeyguardAuthenticated() {
-        mBouncer.notifyKeyguardAuthenticated();
+    public void notifyKeyguardAuthenticated(boolean strongAuth) {
+        mBouncer.notifyKeyguardAuthenticated(strongAuth);
     }
 
-    public void setWakeAndUnlocking() {
-        mWakeAndUnlocking = true;
-        mScrimController.setWakeAndUnlocking();
-        if (mPhoneStatusBar.getNavigationBarView() != null) {
-            mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
-        }
+    public void showBouncerMessage(String message, int color) {
+        mBouncer.showMessage(message, color);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 038fefb..ccfa0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -24,6 +24,7 @@
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.view.WindowManager;
 
 import com.android.keyguard.R;
@@ -47,13 +48,15 @@
     private WindowManager.LayoutParams mLpChanged;
     private int mBarHeight;
     private final boolean mKeyguardScreenRotation;
-
+    private final float mScreenBrightnessDoze;
     private final State mCurrentState = new State();
 
     public StatusBarWindowManager(Context context) {
         mContext = context;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
+        mScreenBrightnessDoze = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
     }
 
     private boolean shouldEnableKeyguardScreenRotation() {
@@ -182,6 +185,7 @@
         applyInputFeatures(state);
         applyFitsSystemWindows(state);
         applyModalFlag(state);
+        applyBrightness(state);
         if (mLp.copyFrom(mLpChanged) != 0) {
             mWindowManager.updateViewLayout(mStatusBarView, mLp);
         }
@@ -205,6 +209,14 @@
         }
     }
 
+    private void applyBrightness(State state) {
+        if (state.forceDozeBrightness) {
+            mLpChanged.screenBrightness = mScreenBrightnessDoze;
+        } else {
+            mLpChanged.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
+        }
+    }
+
     public void setKeyguardShowing(boolean showing) {
         mCurrentState.keyguardShowing = showing;
         apply(mCurrentState);
@@ -279,6 +291,15 @@
         apply(mCurrentState);
     }
 
+    /**
+     * Set whether the screen brightness is forced to the value we use for doze mode by the status
+     * bar window.
+     */
+    public void setForceDozeBrightness(boolean forceDozeBrightness) {
+        mCurrentState.forceDozeBrightness = forceDozeBrightness;
+        apply(mCurrentState);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("StatusBarWindowManager state:");
         pw.println(mCurrentState);
@@ -297,6 +318,7 @@
         boolean headsUpShowing;
         boolean forceStatusBarVisible;
         boolean forceCollapsed;
+        boolean forceDozeBrightness;
 
         /**
          * The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index bd537f7..a91cd51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -132,7 +132,7 @@
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+        public void onFingerprintAuthenticated(int userId) {
             if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
                 return;
             }
@@ -143,6 +143,11 @@
         public void onFaceUnlockStateChanged(boolean running, int userId) {
             update(false /* updateAlways */);
         }
+
+        @Override
+        public void onStrongAuthStateChanged(int userId) {
+            update(false /* updateAlways */);
+        }
     };
 
     public boolean isTrustManaged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 753a7f7..56f6564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -34,6 +34,7 @@
     boolean hasDelays;
     boolean hasGoToFullShadeEvent;
     boolean hasDarkEvent;
+    boolean hasHeadsUpDisappearClickEvent;
     int darkAnimationOriginIndex;
 
     public AnimationFilter animateAlpha() {
@@ -106,6 +107,10 @@
                 hasDarkEvent = true;
                 darkAnimationOriginIndex = ev.darkAnimationOriginIndex;
             }
+            if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+                    .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+                hasHeadsUpDisappearClickEvent = true;
+            }
         }
     }
 
@@ -135,6 +140,7 @@
         hasDelays = false;
         hasGoToFullShadeEvent = false;
         hasDarkEvent = false;
+        hasHeadsUpDisappearClickEvent = false;
         darkAnimationOriginIndex =
                 NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 23d9b9f..a7fe71e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -653,7 +653,7 @@
 
     @Override
     public float getFalsingThresholdFactor() {
-        return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+        return mPhoneStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
     }
 
     public View getChildAtPosition(MotionEvent ev) {
@@ -1915,7 +1915,9 @@
             boolean onBottom = false;
             boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
             if (!mIsExpanded && !isHeadsUp) {
-                type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+                type = row.wasJustClicked()
+                        ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                        : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
             } else {
                 StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
                 if (viewState == null) {
@@ -3016,6 +3018,15 @@
                         .animateY()
                         .animateZ(),
 
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ()
+                        .hasDelays(),
+
                 // ANIMATION_TYPE_HEADS_UP_OTHER
                 new AnimationFilter()
                         .animateAlpha()
@@ -3087,6 +3098,9 @@
                 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
                 StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
 
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+
                 // ANIMATION_TYPE_HEADS_UP_OTHER
                 StackStateAnimator.ANIMATION_DURATION_STANDARD,
 
@@ -3110,8 +3124,9 @@
         static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
         static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
         static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
-        static final int ANIMATION_TYPE_HEADS_UP_OTHER = 16;
-        static final int ANIMATION_TYPE_EVERYTHING = 17;
+        static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
+        static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
+        static final int ANIMATION_TYPE_EVERYTHING = 18;
 
         static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
         static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 82064a7..cf696a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -127,7 +127,7 @@
         mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.notification_collapse_second_card_padding);
         mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi
-                >= DisplayMetrics.DENSITY_XXHIGH;
+                >= DisplayMetrics.DENSITY_420;
     }
 
     public boolean shouldScaleDimmed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 97c7d30..b4ab48a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -54,6 +54,7 @@
     public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
     public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
     public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN = 3;
+    public static final int ANIMATION_DELAY_HEADS_UP = 120;
 
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
     private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
@@ -320,6 +321,9 @@
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
+        if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
+            return ANIMATION_DELAY_HEADS_UP;
+        }
         long minDelay = 0;
         for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
             long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
@@ -890,7 +894,9 @@
                 mHeadsUpAppearChildren.add(changingView);
                 finalState.applyState(changingView, mTmpState);
             } else if (event.animationType == NotificationStackScrollLayout
-                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
+                    event.animationType == NotificationStackScrollLayout
+                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
                 mHeadsUpDisappearChildren.add(changingView);
                 if (mHostLayout.indexOfChild(changingView) == -1) {
                     // This notification was actually removed, so we need to add it to the overlay
@@ -900,7 +906,11 @@
                     // We temporarily enable Y animations, the real filter will be combined
                     // afterwards anyway
                     mAnimationFilter.animateY = true;
-                    startViewAnimations(changingView, mTmpState, 0,
+                    startViewAnimations(changingView, mTmpState,
+                            event.animationType == NotificationStackScrollLayout
+                                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+                                            ? ANIMATION_DELAY_HEADS_UP
+                                            : 0,
                             ANIMATION_DURATION_HEADS_UP_DISAPPEAR);
                     mChildrenToClearFromOverlay.add(changingView);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 920b682..2587b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,6 +171,10 @@
     }
 
     @Override
+    public void onCameraLaunchGestureDetected() {
+    }
+
+    @Override
     protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
             boolean alertAgain) {
     }
diff --git a/packages/WallpaperCropper/AndroidManifest.xml b/packages/WallpaperCropper/AndroidManifest.xml
index 81d1085..e558d7e 100644
--- a/packages/WallpaperCropper/AndroidManifest.xml
+++ b/packages/WallpaperCropper/AndroidManifest.xml
@@ -21,7 +21,10 @@
         <uses-permission android:name="android.permission.SET_WALLPAPER" />
         <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
 
-        <application android:requiredForAllUsers="true">
+        <application
+            android:requiredForAllUsers="true"
+            android:largeHeap="true">
+
         <activity
             android:name="WallpaperCropActivity"
             android:theme="@style/Theme.WallpaperCropper"
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 64b6134..666f2ff 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -9,7 +9,7 @@
     java/com/android/server/EventLogTags.logtags \
     java/com/android/server/am/EventLogTags.logtags
 
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := services.net telephony-common
 LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 56ebed6..c373fb8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1569,6 +1569,8 @@
                 if ("-h".equals(arg)) {
                     dumpHelp(pw);
                     return;
+                } else if ("-a".equals(arg)) {
+                    // dump all data
                 } else if ("write-settings".equals(arg)) {
                     long token = Binder.clearCallingIdentity();
                     try {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index b3b4651..2eeaec9 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -120,6 +120,7 @@
     private int mLastBatteryVoltage;
     private int mLastBatteryTemperature;
     private boolean mLastBatteryLevelCritical;
+    private int mLastMaxChargingCurrent;
 
     private int mInvalidCharger;
     private int mLastInvalidCharger;
@@ -323,6 +324,7 @@
                     + "chargerAcOnline=" + mBatteryProps.chargerAcOnline
                     + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
                     + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+                    + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
                     + ", batteryStatus=" + mBatteryProps.batteryStatus
                     + ", batteryHealth=" + mBatteryProps.batteryHealth
                     + ", batteryPresent=" + mBatteryProps.batteryPresent
@@ -353,6 +355,7 @@
                 mPlugType != mLastPlugType ||
                 mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
+                mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
                 mInvalidCharger != mLastInvalidCharger)) {
 
             if (mPlugType != mLastPlugType) {
@@ -479,6 +482,7 @@
             mLastPlugType = mPlugType;
             mLastBatteryVoltage = mBatteryProps.batteryVoltage;
             mLastBatteryTemperature = mBatteryProps.batteryTemperature;
+            mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
             mLastInvalidCharger = mInvalidCharger;
         }
@@ -503,17 +507,21 @@
         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
+        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
 
         if (DEBUG) {
             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +
                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
-                    ", health:" + mBatteryProps.batteryHealth +  ", present:" + mBatteryProps.batteryPresent +
+                    ", health:" + mBatteryProps.batteryHealth +
+                    ", present:" + mBatteryProps.batteryPresent +
                     ", voltage: " + mBatteryProps.batteryVoltage +
                     ", temperature: " + mBatteryProps.batteryTemperature +
                     ", technology: " + mBatteryProps.batteryTechnology +
-                    ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +
+                    ", AC powered:" + mBatteryProps.chargerAcOnline +
+                    ", USB powered:" + mBatteryProps.chargerUsbOnline +
                     ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
-                    ", icon:" + icon  + ", invalid charger:" + mInvalidCharger);
+                    ", icon:" + icon  + ", invalid charger:" + mInvalidCharger +
+                    ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent);
         }
 
         mHandler.post(new Runnable() {
@@ -618,6 +626,7 @@
                 pw.println("  AC powered: " + mBatteryProps.chargerAcOnline);
                 pw.println("  USB powered: " + mBatteryProps.chargerUsbOnline);
                 pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);
+                pw.println("  Max charging current: " + mBatteryProps.maxChargingCurrent);
                 pw.println("  status: " + mBatteryProps.batteryStatus);
                 pw.println("  health: " + mBatteryProps.batteryHealth);
                 pw.println("  present: " + mBatteryProps.batteryPresent);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4919bed..e19447d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -47,6 +47,7 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -117,6 +118,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
@@ -148,6 +150,8 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -399,6 +403,8 @@
 
     TelephonyManager mTelephonyManager;
 
+    private KeepaliveTracker mKeepaliveTracker;
+
     // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
     private final static int MIN_NET_ID = 100; // some reserved marks
     private final static int MAX_NET_ID = 65535;
@@ -764,6 +770,8 @@
         mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
 
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
+        mKeepaliveTracker = new KeepaliveTracker(mHandler);
     }
 
     private NetworkRequest createInternetRequestForTransport(int transportType) {
@@ -1450,6 +1458,10 @@
                 "ConnectivityService");
     }
 
+    private void enforceKeepalivePermission() {
+        mContext.enforceCallingPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
+    }
+
     public void sendConnectedBroadcast(NetworkInfo info) {
         enforceConnectivityInternalPermission();
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
@@ -1841,10 +1853,13 @@
                 pw.println(", last requested never");
             }
         }
-        pw.println();
 
+        pw.println();
         mTethering.dump(fd, pw, args);
 
+        pw.println();
+        mKeepaliveTracker.dump(pw);
+
         if (mInetLog != null && mInetLog.size() > 0) {
             pw.println();
             pw.println("Inet condition reports:");
@@ -1922,7 +1937,12 @@
                                 (NetworkCapabilities)msg.obj;
                         if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
                                 networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
-                            Slog.wtf(TAG, "BUG: " + nai + " has stateful capability.");
+                            Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
+                        }
+                        if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
+                                networkCapabilities)) {
+                            Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
+                                    + nai.networkCapabilities + " -> " + networkCapabilities);
                         }
                         updateCapabilities(nai, networkCapabilities);
                     }
@@ -2006,6 +2026,15 @@
                     nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
                     break;
                 }
+                case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("EVENT_PACKET_KEEPALIVE from unknown NetworkAgent");
+                        break;
+                    }
+                    mKeepaliveTracker.handleEventPacketKeepalive(nai, msg);
+                    break;
+                }
                 case NetworkMonitor.EVENT_NETWORK_TESTED: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
                     if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
@@ -2148,6 +2177,8 @@
             // sending all CALLBACK_LOST messages (for requests, not listens) at the end
             // of rematchAllNetworksAndRequests
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
+            mKeepaliveTracker.handleStopAllKeepalives(nai,
+                    ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
             mNetworkAgentInfos.remove(msg.replyTo);
             updateClat(null, nai.linkProperties, nai);
@@ -2226,6 +2257,13 @@
     private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
         mNetworkRequests.put(nri.request, nri);
         mNetworkRequestInfoLogs.log("REGISTER " + nri);
+        if (!nri.isRequest) {
+            for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
+                if (network.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                    updateSignalStrengthThresholds(network);
+                }
+            }
+        }
         rematchAllNetworksAndRequests(null, 0);
         if (nri.isRequest && mNetworkForRequestId.get(nri.request.requestId) == null) {
             sendUpdatedScoreToFactories(nri.request, 0);
@@ -2339,6 +2377,9 @@
                 // if this listen request applies and remove it.
                 for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                     nai.networkRequests.remove(nri.request.requestId);
+                    if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                        updateSignalStrengthThresholds(nai);
+                    }
                 }
             }
             callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
@@ -2505,6 +2546,19 @@
                     handleMobileDataAlwaysOn();
                     break;
                 }
+                // Sent by KeepaliveTracker to process an app request on the state machine thread.
+                case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
+                    mKeepaliveTracker.handleStartKeepalive(msg);
+                    break;
+                }
+                // Sent by KeepaliveTracker to process an app request on the state machine thread.
+                case NetworkAgent.CMD_STOP_PACKET_KEEPALIVE: {
+                    NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
+                    int slot = msg.arg1;
+                    int reason = msg.arg2;
+                    mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
+                    break;
+                }
                 case EVENT_SYSTEM_READY: {
                     for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                         nai.networkMonitor.systemReady = true;
@@ -3554,15 +3608,32 @@
         }
     }
 
-    private void ensureImmutableCapabilities(NetworkCapabilities networkCapabilities) {
-        if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
-            throw new IllegalArgumentException(
-                    "Cannot request network with NET_CAPABILITY_VALIDATED");
+    private void ensureRequestableCapabilities(NetworkCapabilities networkCapabilities) {
+        final String badCapability = networkCapabilities.describeFirstNonRequestableCapability();
+        if (badCapability != null) {
+            throw new IllegalArgumentException("Cannot request network with " + badCapability);
         }
-        if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
-            throw new IllegalArgumentException(
-                    "Cannot request network with NET_CAPABILITY_CAPTIVE_PORTAL");
+    }
+
+    private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
+        final SortedSet<Integer> thresholds = new TreeSet();
+        synchronized (nai) {
+            for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+                if (nri.request.networkCapabilities.hasSignalStrength() &&
+                        nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                    thresholds.add(nri.request.networkCapabilities.getSignalStrength());
+                }
+            }
         }
+        return new ArrayList<Integer>(thresholds);
+    }
+
+    private void updateSignalStrengthThresholds(NetworkAgentInfo nai) {
+        Bundle thresholds = new Bundle();
+        thresholds.putIntegerArrayList("thresholds", getSignalStrengthThresholds(nai));
+        nai.asyncChannel.sendMessage(
+                android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS,
+                0, 0, thresholds);
     }
 
     @Override
@@ -3571,7 +3642,7 @@
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         enforceNetworkRequestPermissions(networkCapabilities);
         enforceMeteredApnPolicy(networkCapabilities);
-        ensureImmutableCapabilities(networkCapabilities);
+        ensureRequestableCapabilities(networkCapabilities);
 
         if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
             throw new IllegalArgumentException("Bad timeout specified");
@@ -3640,7 +3711,7 @@
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         enforceNetworkRequestPermissions(networkCapabilities);
         enforceMeteredApnPolicy(networkCapabilities);
-        ensureImmutableCapabilities(networkCapabilities);
+        ensureRequestableCapabilities(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId());
@@ -3866,6 +3937,8 @@
             notifyIfacesChanged();
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
         }
+
+        mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
     }
 
     private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
@@ -4532,6 +4605,15 @@
                 // TODO: support proxy per network.
             }
 
+            // Whether a particular NetworkRequest listen should cause signal strength thresholds to
+            // be communicated to a particular NetworkAgent depends only on the network's immutable,
+            // capabilities, so it only needs to be done once on initial connect, not every time the
+            // network's capabilities change. Note that we do this before rematching the network,
+            // so we could decide to tear it down immediately afterwards. That's fine though - on
+            // disconnection NetworkAgents should stop any signal strength monitoring they have been
+            // doing.
+            updateSignalStrengthThresholds(networkAgent);
+
             // Consider network even though it is not yet validated.
             rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
 
@@ -4711,6 +4793,22 @@
     }
 
     @Override
+    public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger,
+            IBinder binder, String srcAddr, int srcPort, String dstAddr) {
+        enforceKeepalivePermission();
+        mKeepaliveTracker.startNattKeepalive(
+                getNetworkAgentInfoForNetwork(network),
+                intervalSeconds, messenger, binder,
+                srcAddr, srcPort, dstAddr, ConnectivityManager.PacketKeepalive.NATT_PORT);
+    }
+
+    @Override
+    public void stopKeepalive(Network network, int slot) {
+        mHandler.sendMessage(mHandler.obtainMessage(
+                NetworkAgent.CMD_STOP_PACKET_KEEPALIVE, slot, PacketKeepalive.SUCCESS, network));
+    }
+
+    @Override
     public void factoryReset() {
         enforceConnectivityInternalPermission();
 
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 7aebc1a..ab2ea8b 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -244,3 +244,8 @@
 # ---------------------------
 40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3)
 40001 stream_devices_changed (stream|1), (prev_devices|1), (devices|1)
+
+# ---------------------------
+# GestureLauncherService.java
+# ---------------------------
+40100 camera_gesture_triggered (gesture_on_time|2|3), (sensor1_on_time|2|3), (sensor2_on_time|2|3), (event_extra|1|1)
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
new file mode 100644
index 0000000..342a3ef
--- /dev/null
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+/**
+ * The service that listens for gestures detected in sensor firmware and starts the intent
+ * accordingly.
+ * <p>For now, only camera launch gesture is supported, and in the future, more gestures can be
+ * added.</p>
+ * @hide
+ */
+class GestureLauncherService extends SystemService {
+    private static final boolean DBG = false;
+    private static final String TAG = "GestureLauncherService";
+
+    /** The listener that receives the gesture event. */
+    private final GestureEventListener mGestureListener = new GestureEventListener();
+
+    private Sensor mCameraLaunchSensor;
+    private Context mContext;
+
+    /** The wake lock held when a gesture is detected. */
+    private WakeLock mWakeLock;
+    private boolean mRegistered;
+    private int mUserId;
+
+    // Below are fields used for event logging only.
+    /** Elapsed real time when the camera gesture is turned on. */
+    private long mCameraGestureOnTimeMs = 0L;
+
+    /** Elapsed real time when the last camera gesture was detected. */
+    private long mCameraGestureLastEventTime = 0L;
+
+    /**
+     * How long the sensor 1 has been turned on since camera launch sensor was
+     * subscribed to and when the last camera launch gesture was detected.
+     * <p>Sensor 1 is the main sensor used to detect camera launch gesture.</p>
+     */
+    private long mCameraGestureSensor1LastOnTimeMs = 0L;
+
+    /**
+     * If applicable, how long the sensor 2 has been turned on since camera
+     * launch sensor was subscribed to and when the last camera launch
+     * gesture was detected.
+     * <p>Sensor 2 is the secondary sensor used to detect camera launch gesture.
+     * This is optional and if only sensor 1 is used for detect camera launch
+     * gesture, this value would always be 0.</p>
+     */
+    private long mCameraGestureSensor2LastOnTimeMs = 0L;
+
+    /**
+     * Extra information about the event when the last camera launch gesture
+     * was detected.
+     */
+    private int mCameraLaunchLastEventExtra = 0;
+
+    public GestureLauncherService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    public void onStart() {
+        // Nothing to publish.
+    }
+
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            Resources resources = mContext.getResources();
+            if (!isGestureLauncherEnabled(resources)) {
+                if (DBG) Slog.d(TAG, "Gesture launcher is disabled in system properties.");
+                return;
+            }
+
+            PowerManager powerManager = (PowerManager) mContext.getSystemService(
+                    Context.POWER_SERVICE);
+            mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "GestureLauncherService");
+            updateCameraRegistered();
+
+            mUserId = ActivityManager.getCurrentUser();
+            mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
+            registerContentObserver();
+        }
+    }
+
+    private void registerContentObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+                false, mSettingObserver, mUserId);
+    }
+
+    private void updateCameraRegistered() {
+        Resources resources = mContext.getResources();
+        if (isCameraLaunchSettingEnabled(mContext, mUserId)) {
+            registerCameraLaunchGesture(resources);
+        } else {
+            unregisterCameraLaunchGesture();
+        }
+    }
+
+    private void unregisterCameraLaunchGesture() {
+        if (mRegistered) {
+            mRegistered = false;
+            mCameraGestureOnTimeMs = 0L;
+            mCameraGestureLastEventTime = 0L;
+            mCameraGestureSensor1LastOnTimeMs = 0;
+            mCameraGestureSensor2LastOnTimeMs = 0;
+            mCameraLaunchLastEventExtra = 0;
+
+            SensorManager sensorManager = (SensorManager) mContext.getSystemService(
+                    Context.SENSOR_SERVICE);
+            sensorManager.unregisterListener(mGestureListener);
+        }
+    }
+
+    /**
+     * Registers for the camera launch gesture.
+     */
+    private void registerCameraLaunchGesture(Resources resources) {
+        if (mRegistered) {
+            return;
+        }
+        mCameraGestureOnTimeMs = SystemClock.elapsedRealtime();
+        mCameraGestureLastEventTime = mCameraGestureOnTimeMs;
+        SensorManager sensorManager = (SensorManager) mContext.getSystemService(
+                Context.SENSOR_SERVICE);
+        int cameraLaunchGestureId = resources.getInteger(
+                com.android.internal.R.integer.config_cameraLaunchGestureSensorType);
+        if (cameraLaunchGestureId != -1) {
+            mRegistered = false;
+            String sensorName = resources.getString(
+                com.android.internal.R.string.config_cameraLaunchGestureSensorStringType);
+            mCameraLaunchSensor = sensorManager.getDefaultSensor(
+                    cameraLaunchGestureId,
+                    true /*wakeUp*/);
+
+            // Compare the camera gesture string type to that in the resource file to make
+            // sure we are registering the correct sensor. This is redundant check, it
+            // makes the code more robust.
+            if (mCameraLaunchSensor != null) {
+                if (sensorName.equals(mCameraLaunchSensor.getStringType())) {
+                    mRegistered = sensorManager.registerListener(mGestureListener,
+                            mCameraLaunchSensor, 0);
+                } else {
+                    String message = String.format("Wrong configuration. Sensor type and sensor "
+                            + "string type don't match: %s in resources, %s in the sensor.",
+                            sensorName, mCameraLaunchSensor.getStringType());
+                    throw new RuntimeException(message);
+                }
+            }
+            if (DBG) Slog.d(TAG, "Camera launch sensor registered: " + mRegistered);
+        } else {
+            if (DBG) Slog.d(TAG, "Camera launch sensor is not specified.");
+        }
+    }
+
+    public static boolean isCameraLaunchSettingEnabled(Context context, int userId) {
+        return isCameraLaunchEnabled(context.getResources())
+                && (Settings.Secure.getIntForUser(context.getContentResolver(),
+                        Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
+    }
+
+    /**
+     * Whether to enable the camera launch gesture.
+     */
+    public static boolean isCameraLaunchEnabled(Resources resources) {
+        boolean configSet = resources.getInteger(
+                com.android.internal.R.integer.config_cameraLaunchGestureSensorType) != -1;
+        return configSet &&
+                !SystemProperties.getBoolean("gesture.disable_camera_launch", false);
+    }
+
+    /**
+     * Whether GestureLauncherService should be enabled according to system properties.
+     */
+    public static boolean isGestureLauncherEnabled(Resources resources) {
+        // For now, the only supported gesture is camera launch gesture, so whether to enable this
+        // service equals to isCameraLaunchEnabled();
+        return isCameraLaunchEnabled(resources);
+    }
+
+    private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+                mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+                registerContentObserver();
+                updateCameraRegistered();
+            }
+        }
+    };
+
+    private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) {
+        public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
+            if (userId == mUserId) {
+                updateCameraRegistered();
+            }
+        }
+    };
+
+    private final class GestureEventListener implements SensorEventListener {
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            if (!mRegistered) {
+              if (DBG) Slog.d(TAG, "Ignoring gesture event because it's unregistered.");
+              return;
+            }
+            if (event.sensor == mCameraLaunchSensor) {
+                handleCameraLaunchGesture(event);
+                return;
+            }
+        }
+
+        private void handleCameraLaunchGesture(SensorEvent event) {
+            if (DBG) {
+                float[] values = event.values;
+                Slog.d(TAG, String.format("Received a camera launch event: " +
+                      "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
+            }
+            boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+            if (!userSetupComplete) {
+                if (DBG) Slog.d(TAG, String.format(
+                        "userSetupComplete = %s, ignoring camera launch gesture.",
+                        userSetupComplete));
+                return;
+            }
+            if (DBG) Slog.d(TAG, String.format(
+                    "userSetupComplete = %s, performing camera launch gesture.",
+                    userSetupComplete));
+
+            // Make sure we don't sleep too early
+            mWakeLock.acquire(500L);
+            StatusBarManagerInternal service = LocalServices.getService(
+                    StatusBarManagerInternal.class);
+            service.onCameraLaunchGestureDetected();
+            trackCameraLaunchEvent(event);
+            mWakeLock.release();
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // Ignored.
+        }
+
+        private void trackCameraLaunchEvent(SensorEvent event) {
+            long now = SystemClock.elapsedRealtime();
+            long totalDuration = now - mCameraGestureOnTimeMs;
+            // values[0]: ratio between total time duration when accel is turned on and time
+            //            duration since camera launch gesture is subscribed.
+            // values[1]: ratio between total time duration when gyro is turned on and time duration
+            //            since camera launch gesture is subscribed.
+            // values[2]: extra information
+            float[] values = event.values;
+
+            long sensor1OnTime = (long) (totalDuration * (double) values[0]);
+            long sensor2OnTime = (long) (totalDuration * (double) values[1]);
+            int extra = (int) values[2];
+
+            // We only log the difference in the event log to make aggregation easier.
+            long gestureOnTimeDiff = now - mCameraGestureLastEventTime;
+            long sensor1OnTimeDiff = sensor1OnTime - mCameraGestureSensor1LastOnTimeMs;
+            long sensor2OnTimeDiff = sensor2OnTime - mCameraGestureSensor2LastOnTimeMs;
+            int extraDiff = extra - mCameraLaunchLastEventExtra;
+
+            // Gating against negative time difference. This doesn't usually happen, but it may
+            // happen because of numeric errors.
+            if (gestureOnTimeDiff < 0 || sensor1OnTimeDiff < 0 || sensor2OnTimeDiff < 0) {
+                if (DBG) Slog.d(TAG, "Skipped event logging because negative numbers.");
+                return;
+            }
+
+            if (DBG) Slog.d(TAG, String.format("totalDuration: %d, sensor1OnTime: %s, " +
+                    "sensor2OnTime: %d, extra: %d",
+                    gestureOnTimeDiff,
+                    sensor1OnTimeDiff,
+                    sensor2OnTimeDiff,
+                    extraDiff));
+            EventLogTags.writeCameraGestureTriggered(
+                    gestureOnTimeDiff,
+                    sensor1OnTimeDiff,
+                    sensor2OnTimeDiff,
+                    extraDiff);
+
+            mCameraGestureLastEventTime = now;
+            mCameraGestureSensor1LastOnTimeMs = sensor1OnTime;
+            mCameraGestureSensor2LastOnTimeMs = sensor2OnTime;
+            mCameraLaunchLastEventExtra = extra;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cae060a..468ead0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -542,22 +542,25 @@
             Slog.e(TAG,  "Unable to bind FLP Geofence proxy.");
         }
 
-        // bind to the hardware activity recognition if supported
-        if (ActivityRecognitionHardware.isSupported()) {
-            ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
-                    mContext,
-                    mLocationHandler,
-                    ActivityRecognitionHardware.getInstance(mContext),
-                    com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
-                    com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
-                    com.android.internal.R.array.config_locationProviderPackageNames);
-
-            if (proxy == null) {
-                Slog.e(TAG, "Unable to bind ActivityRecognitionProxy.");
-            }
+        // bind to hardware activity recognition
+        boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported();
+        ActivityRecognitionHardware activityRecognitionHardware = null;
+        if (activityRecognitionHardwareIsSupported) {
+            activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext);
         } else {
             Slog.e(TAG, "Hardware Activity-Recognition not supported.");
         }
+        ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
+                mContext,
+                mLocationHandler,
+                activityRecognitionHardwareIsSupported,
+                activityRecognitionHardware,
+                com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
+                com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
+                com.android.internal.R.array.config_locationProviderPackageNames);
+        if (proxy == null) {
+            Slog.e(TAG, "Unable to bind ActivityRecognitionProxy.");
+        }
 
         String[] testProviderStrings = resources.getStringArray(
                 com.android.internal.R.array.config_testLocationProviders);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 5e2fe5a..f1d7da4 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -18,6 +18,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.app.backup.BackupManager;
+import android.app.trust.IStrongAuthTracker;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -28,6 +29,8 @@
 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
 import static android.content.Context.USER_SERVICE;
 import static android.Manifest.permission.READ_CONTACTS;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Binder;
 import android.os.IBinder;
@@ -70,6 +73,7 @@
     private final Context mContext;
 
     private final LockSettingsStorage mStorage;
+    private final LockSettingsStrongAuth mStrongAuth = new LockSettingsStrongAuth();
 
     private LockPatternUtils mLockPatternUtils;
     private boolean mFirstCallToVold;
@@ -93,6 +97,7 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_STARTING);
         filter.addAction(Intent.ACTION_USER_REMOVED);
+        filter.addAction(Intent.ACTION_USER_PRESENT);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
 
         mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
@@ -122,6 +127,8 @@
             } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 mStorage.prefetchUser(userHandle);
+            } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
+                mStrongAuth.reportUnlock(getSendingUserId());
             } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 if (userHandle > 0) {
@@ -659,6 +666,10 @@
             if (shouldReEnroll) {
                 credentialUtil.setCredential(credential, credential, userId);
             }
+        } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
+            if (response.getTimeout() > 0) {
+                requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
+            }
         }
 
         return response;
@@ -713,6 +724,7 @@
 
     private void removeUser(int userId) {
         mStorage.removeUser(userId);
+        mStrongAuth.removeUser(userId);
 
         final KeyStore ks = KeyStore.getInstance();
         ks.onUserRemoved(userId);
@@ -727,6 +739,24 @@
         }
     }
 
+    @Override
+    public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
+        checkPasswordReadPermission(UserHandle.USER_ALL);
+        mStrongAuth.registerStrongAuthTracker(tracker);
+    }
+
+    @Override
+    public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
+        checkPasswordReadPermission(UserHandle.USER_ALL);
+        mStrongAuth.unregisterStrongAuthTracker(tracker);
+    }
+
+    @Override
+    public void requireStrongAuth(int strongAuthReason, int userId) {
+        checkWritePermission(userId);
+        mStrongAuth.requireStrongAuth(strongAuthReason, userId);
+    }
+
     private static final String[] VALID_SETTINGS = new String[] {
         LockPatternUtils.LOCKOUT_PERMANENT_KEY,
         LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
@@ -797,5 +827,4 @@
         Slog.e(TAG, "Unable to acquire GateKeeperService");
         return null;
     }
-
 }
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
new file mode 100644
index 0000000..c023f4a
--- /dev/null
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
+
+import android.app.trust.IStrongAuthTracker;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+
+/**
+ * Keeps track of requests for strong authentication.
+ */
+public class LockSettingsStrongAuth {
+
+    private static final String TAG = "LockSettings";
+
+    private static final int MSG_REQUIRE_STRONG_AUTH = 1;
+    private static final int MSG_REGISTER_TRACKER = 2;
+    private static final int MSG_UNREGISTER_TRACKER = 3;
+    private static final int MSG_REMOVE_USER = 4;
+
+    private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
+    private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
+
+    private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
+        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
+            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
+                return;
+            }
+        }
+        mStrongAuthTrackers.add(tracker);
+
+        for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+            int key = mStrongAuthForUser.keyAt(i);
+            int value = mStrongAuthForUser.valueAt(i);
+            try {
+                tracker.onStrongAuthRequiredChanged(value, key);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception while adding StrongAuthTracker.", e);
+            }
+        }
+    }
+
+    private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
+        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
+            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
+                mStrongAuthTrackers.remove(i);
+                return;
+            }
+        }
+    }
+
+    private void handleRequireStrongAuth(int strongAuthReason, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+                int key = mStrongAuthForUser.keyAt(i);
+                handleRequireStrongAuthOneUser(strongAuthReason, key);
+            }
+        } else {
+            handleRequireStrongAuthOneUser(strongAuthReason, userId);
+        }
+    }
+
+    private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) {
+        int oldValue = mStrongAuthForUser.get(userId, LockPatternUtils.StrongAuthTracker.DEFAULT);
+        int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED
+                ? STRONG_AUTH_NOT_REQUIRED
+                : (oldValue | strongAuthReason);
+        if (oldValue != newValue) {
+            mStrongAuthForUser.put(userId, newValue);
+            notifyStrongAuthTrackers(newValue, userId);
+        }
+    }
+
+    private void handleRemoveUser(int userId) {
+        int index = mStrongAuthForUser.indexOfKey(userId);
+        if (index >= 0) {
+            mStrongAuthForUser.removeAt(index);
+            notifyStrongAuthTrackers(StrongAuthTracker.DEFAULT, userId);
+        }
+    }
+
+    private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
+        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
+            try {
+                mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId);
+            } catch (DeadObjectException e) {
+                Slog.d(TAG, "Removing dead StrongAuthTracker.");
+                mStrongAuthTrackers.remove(i);
+                i--;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
+            }
+        }
+    }
+
+    public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
+        mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget();
+    }
+
+    public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
+        mHandler.obtainMessage(MSG_UNREGISTER_TRACKER, tracker).sendToTarget();
+    }
+
+    public void removeUser(int userId) {
+        mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
+    }
+
+    public void requireStrongAuth(int strongAuthReason, int userId) {
+        if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
+            mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
+                    userId).sendToTarget();
+        } else {
+            throw new IllegalArgumentException(
+                    "userId must be an explicit user id or USER_ALL");
+        }
+    }
+
+    public void reportUnlock(int userId) {
+        requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_REGISTER_TRACKER:
+                    handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj);
+                    break;
+                case MSG_UNREGISTER_TRACKER:
+                    handleRemoveStrongAuthTracker((IStrongAuthTracker) msg.obj);
+                    break;
+                case MSG_REQUIRE_STRONG_AUTH:
+                    handleRequireStrongAuth(msg.arg1, msg.arg2);
+                    break;
+                case MSG_REMOVE_USER:
+                    handleRemoveUser(msg.arg1);
+                    break;
+            }
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d10a457..0d64540 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2582,6 +2582,63 @@
     }
 
     @Override
+    public void createNewUserDir(int userHandle, String path) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only SYSTEM_UID can create user directories");
+        }
+
+        waitForReady();
+
+        if (DEBUG_EVENTS) {
+            Slog.i(TAG, "Creating new user dir");
+        }
+
+        try {
+            NativeDaemonEvent event = mCryptConnector.execute(
+                "cryptfs", "createnewuserdir", userHandle, path);
+            if (!"0".equals(event.getMessage())) {
+                String error = "createnewuserdir sent unexpected message: "
+                    + event.getMessage();
+                Slog.e(TAG,  error);
+                // ext4enc:TODO is this the right exception?
+                throw new RuntimeException(error);
+            }
+        } catch (NativeDaemonConnectorException e) {
+            Slog.e(TAG, "createnewuserdir threw exception", e);
+            throw new RuntimeException("createnewuserdir threw exception", e);
+        }
+    }
+
+    // ext4enc:TODO duplication between this and createNewUserDir is nasty
+    @Override
+    public void deleteUserKey(int userHandle) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only SYSTEM_UID can delete user keys");
+        }
+
+        waitForReady();
+
+        if (DEBUG_EVENTS) {
+            Slog.i(TAG, "Deleting user key");
+        }
+
+        try {
+            NativeDaemonEvent event = mCryptConnector.execute(
+                "cryptfs", "deleteuserkey", userHandle);
+            if (!"0".equals(event.getMessage())) {
+                String error = "deleteuserkey sent unexpected message: "
+                    + event.getMessage();
+                Slog.e(TAG,  error);
+                // ext4enc:TODO is this the right exception?
+                throw new RuntimeException(error);
+            }
+        } catch (NativeDaemonConnectorException e) {
+            Slog.e(TAG, "deleteuserkey threw exception", e);
+            throw new RuntimeException("deleteuserkey threw exception", e);
+        }
+    }
+
+    @Override
     public int mkdirs(String callingPkg, String appPath) {
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
         final UserEnvironment userEnv = new UserEnvironment(userId);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9bf4f5f..335288d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1263,8 +1263,8 @@
                     Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
                 }
 
-                // There is some accuracy error in reports so allow 30 milliseconds of error.
-                final long SAMPLE_ERROR_MILLIS = 30;
+                // There is some accuracy error in reports so allow some slop in the results.
+                final long SAMPLE_ERROR_MILLIS = 750;
                 final long totalTimeMs = result.mControllerIdleTimeMs + result.mControllerRxTimeMs +
                         result.mControllerTxTimeMs;
                 if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
index 0be24f4..f82454a 100644
--- a/services/core/java/com/android/server/camera/CameraService.java
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -23,13 +23,17 @@
 import android.content.pm.UserInfo;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
+import android.nfc.INfcAdapter;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Binder;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.os.SystemProperties;
 import android.util.Slog;
+import android.util.ArraySet;
 
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
@@ -44,8 +48,10 @@
  *
  * @hide
  */
-public class CameraService extends SystemService implements Handler.Callback {
+public class CameraService extends SystemService
+        implements Handler.Callback, IBinder.DeathRecipient {
     private static final String TAG = "CameraService_proxy";
+    private static final boolean DEBUG = false;
 
     /**
      * This must match the ICameraService.aidl definition
@@ -58,6 +64,16 @@
     public static final int NO_EVENT = 0; // NOOP
     public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle
 
+    // State arguments to use with the notifyCameraState call from camera service:
+    public static final int CAMERA_STATE_OPEN = 0;
+    public static final int CAMERA_STATE_ACTIVE = 1;
+    public static final int CAMERA_STATE_IDLE = 2;
+    public static final int CAMERA_STATE_CLOSED = 3;
+
+    // Flags arguments to NFC adapter to enable/disable NFC
+    public static final int DISABLE_POLLING_FLAGS = 0x1000;
+    public static final int ENABLE_POLLING_FLAGS = 0x0000;
+
     // Handler message codes
     private static final int MSG_SWITCH_USER = 1;
 
@@ -72,6 +88,17 @@
     private Set<Integer> mEnabledCameraUsers;
     private int mLastUser;
 
+    private ICameraService mCameraServiceRaw;
+
+    private final ArraySet<String> mActiveCameraIds = new ArraySet<>();
+
+    private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
+    private static final String NFC_SERVICE_BINDER_NAME = "nfc";
+    private static final IBinder nfcInterfaceToken = new Binder();
+
+    private final boolean mNotifyNfc;
+    private int mActiveCameraCount = 0;
+
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -102,6 +129,14 @@
         public void pingForUserUpdate() {
             notifySwitchWithRetries(30);
         }
+
+        @Override
+        public void notifyCameraState(String cameraId, int newCameraState) {
+            String state = cameraStateToString(newCameraState);
+            if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state);
+
+            updateActivityCount(cameraId, newCameraState);
+        }
     };
 
     public CameraService(Context context) {
@@ -110,6 +145,9 @@
         mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper(), this);
+
+        mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
+        if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
     }
 
     @Override
@@ -161,13 +199,32 @@
         }
     }
 
+    /**
+     * Handle the death of the native camera service
+     */
+    @Override
+    public void binderDied() {
+        if (DEBUG) Slog.w(TAG, "Native camera service has died");
+        synchronized(mLock) {
+            mCameraServiceRaw = null;
+
+            // All cameras reset to idle on camera service death
+            boolean wasEmpty = mActiveCameraIds.isEmpty();
+            mActiveCameraIds.clear();
+
+            if ( mNotifyNfc && !wasEmpty ) {
+                notifyNfcService(/*enablePolling*/ true);
+            }
+        }
+    }
+
     private void switchUserLocked(int userHandle) {
         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
         mLastUser = userHandle;
         if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
             // Some user handles have been added or removed, update mediaserver.
             mEnabledCameraUsers = currentUserHandles;
-            notifyMediaserver(USER_SWITCHED, currentUserHandles);
+            notifyMediaserverLocked(USER_SWITCHED, currentUserHandles);
         }
     }
 
@@ -187,7 +244,7 @@
             if (mEnabledCameraUsers == null) {
                 return;
             }
-            if (notifyMediaserver(USER_SWITCHED, mEnabledCameraUsers)) {
+            if (notifyMediaserverLocked(USER_SWITCHED, mEnabledCameraUsers)) {
                 retries = 0;
             }
         }
@@ -199,19 +256,27 @@
                 RETRY_DELAY_TIME);
     }
 
-    private boolean notifyMediaserver(int eventType, Set<Integer> updatedUserHandles) {
+    private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
         // Forward the user switch event to the native camera service running in the mediaserver
         // process.
-        IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
-        if (cameraServiceBinder == null) {
-            Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
-            return false; // Camera service not active, cannot evict user clients.
+        if (mCameraServiceRaw == null) {
+            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+            if (cameraServiceBinder == null) {
+                Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
+                return false; // Camera service not active, cannot evict user clients.
+            }
+            try {
+                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not link to death of native camera service");
+                return false;
+            }
+
+            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
         }
 
-        ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
-
         try {
-            cameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
+            mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
         } catch (RemoteException e) {
             Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
             // Not much we can do if camera service is dead.
@@ -220,6 +285,44 @@
         return true;
     }
 
+    private void updateActivityCount(String cameraId, int newCameraState) {
+        synchronized(mLock) {
+            boolean wasEmpty = mActiveCameraIds.isEmpty();
+            switch (newCameraState) {
+                case CAMERA_STATE_OPEN:
+                    break;
+                case CAMERA_STATE_ACTIVE:
+                    mActiveCameraIds.add(cameraId);
+                    break;
+                case CAMERA_STATE_IDLE:
+                case CAMERA_STATE_CLOSED:
+                    mActiveCameraIds.remove(cameraId);
+                    break;
+            }
+            boolean isEmpty = mActiveCameraIds.isEmpty();
+            if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
+                notifyNfcService(isEmpty);
+            }
+        }
+    }
+
+    private void notifyNfcService(boolean enablePolling) {
+
+        IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
+        if (nfcServiceBinder == null) {
+            Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+            return;
+        }
+        INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
+        int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+        if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
+        try {
+            nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
+        }
+    }
+
     private static int[] toArray(Collection<Integer> c) {
         int len = c.size();
         int[] ret = new int[len];
@@ -229,4 +332,15 @@
         }
         return ret;
     }
+
+    private static String cameraStateToString(int newCameraState) {
+        switch (newCameraState) {
+            case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
+            case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
+            case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
+            case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
+            default: break;
+        }
+        return "CAMERA_STATE_UNKNOWN";
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
new file mode 100644
index 0000000..64b9399
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.system.OsConstants;
+import android.net.ConnectivityManager;
+import android.net.NetworkUtils;
+import android.net.util.IpUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+
+/**
+ * Represents the actual packets that are sent by the
+ * {@link android.net.ConnectivityManager.PacketKeepalive} API.
+ *
+ * @hide
+ */
+public class KeepalivePacketData {
+    /** Protocol of the packet to send; one of the OsConstants.ETH_P_* values. */
+    public final int protocol;
+
+    /** Source IP address */
+    public final InetAddress srcAddress;
+
+    /** Destination IP address */
+    public final InetAddress dstAddress;
+
+    /** Source port */
+    public final int srcPort;
+
+    /** Destination port */
+    public final int dstPort;
+
+    /** Destination MAC address. Can change if routing changes. */
+    public byte[] dstMac;
+
+    /** Packet data. A raw byte string of packet data, not including the link-layer header. */
+    public final byte[] data;
+
+    private static final int IPV4_HEADER_LENGTH = 20;
+    private static final int UDP_HEADER_LENGTH = 8;
+
+    protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
+            InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
+        this.srcAddress = srcAddress;
+        this.dstAddress = dstAddress;
+        this.srcPort = srcPort;
+        this.dstPort = dstPort;
+        this.data = data;
+
+        // Check we have two IP addresses of the same family.
+        if (srcAddress == null || dstAddress == null ||
+                !srcAddress.getClass().getName().equals(dstAddress.getClass().getName())) {
+        }
+
+        // Set the protocol.
+        if (this.dstAddress instanceof Inet4Address) {
+            this.protocol = OsConstants.ETH_P_IP;
+        } else if (this.dstAddress instanceof Inet6Address) {
+            this.protocol = OsConstants.ETH_P_IPV6;
+        } else {
+            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+        }
+
+        // Check the ports.
+        if (!IpUtils.isValidUdpOrTcpPort(srcPort) || !IpUtils.isValidUdpOrTcpPort(dstPort)) {
+            throw new InvalidPacketException(ERROR_INVALID_PORT);
+        }
+    }
+
+    public static class InvalidPacketException extends Exception {
+        final public int error;
+        public InvalidPacketException(int error) {
+            this.error = error;
+        }
+    }
+
+    /**
+     * Creates an IPsec NAT-T keepalive packet with the specified parameters.
+     */
+    public static KeepalivePacketData nattKeepalivePacket(
+            InetAddress srcAddress, int srcPort,
+            InetAddress dstAddress, int dstPort) throws InvalidPacketException {
+
+        if (!(srcAddress instanceof Inet4Address)) {
+            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+        }
+
+        if (dstPort != NATT_PORT) {
+            throw new InvalidPacketException(ERROR_INVALID_PORT);
+        }
+
+        int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
+        ByteBuffer buf = ByteBuffer.allocate(length);
+        buf.order(ByteOrder.BIG_ENDIAN);
+        buf.putShort((short) 0x4500);             // IP version and TOS
+        buf.putShort((short) length);
+        buf.putInt(0);                            // ID, flags, offset
+        buf.put((byte) 64);                       // TTL
+        buf.put((byte) OsConstants.IPPROTO_UDP);
+        int ipChecksumOffset = buf.position();
+        buf.putShort((short) 0);                  // IP checksum
+        buf.put(srcAddress.getAddress());
+        buf.put(dstAddress.getAddress());
+        buf.putShort((short) srcPort);
+        buf.putShort((short) dstPort);
+        buf.putShort((short) (length - 20));      // UDP length
+        int udpChecksumOffset = buf.position();
+        buf.putShort((short) 0);                  // UDP checksum
+        buf.put((byte) 0xff);                     // NAT-T keepalive
+        buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+        buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
+
+        return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
new file mode 100644
index 0000000..c78f347
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.connectivity.NetworkAgentInfo;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.PacketKeepalive;
+import android.net.LinkAddress;
+import android.net.NetworkAgent;
+import android.net.NetworkUtils;
+import android.net.util.IpUtils;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.system.OsConstants;
+import android.util.Log;
+import android.util.Pair;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
+import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
+import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
+
+/**
+ * Manages packet keepalive requests.
+ *
+ * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
+ * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
+ * methods must be called only from the ConnectivityService handler thread.
+ */
+public class KeepaliveTracker {
+
+    private static final String TAG = "KeepaliveTracker";
+    private static final boolean DBG = true;
+
+    // TODO: Change this to a system-only permission.
+    public static final String PERMISSION = android.Manifest.permission.CHANGE_NETWORK_STATE;
+
+    /** Keeps track of keepalive requests. */
+    private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
+            new HashMap<> ();
+    private final Handler mConnectivityServiceHandler;
+
+    public KeepaliveTracker(Handler handler) {
+        mConnectivityServiceHandler = handler;
+    }
+
+    /**
+     * Tracks information about a packet keepalive.
+     *
+     * All information about this keepalive is known at construction time except the slot number,
+     * which is only returned when the hardware has successfully started the keepalive.
+     */
+    class KeepaliveInfo implements IBinder.DeathRecipient {
+        // Bookkeping data.
+        private final Messenger mMessenger;
+        private final IBinder mBinder;
+        private final int mUid;
+        private final int mPid;
+        private final NetworkAgentInfo mNai;
+
+        /** Keepalive slot. A small integer that identifies this keepalive among the ones handled
+          * by this network. */
+        private int mSlot = PacketKeepalive.NO_KEEPALIVE;
+
+        // Packet data.
+        private final KeepalivePacketData mPacket;
+        private final int mInterval;
+
+        // Whether the keepalive is started or not.
+        public boolean isStarted;
+
+        public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai,
+                KeepalivePacketData packet, int interval) {
+            mMessenger = messenger;
+            mBinder = binder;
+            mPid = Binder.getCallingPid();
+            mUid = Binder.getCallingUid();
+
+            mNai = nai;
+            mPacket = packet;
+            mInterval = interval;
+
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+
+        public NetworkAgentInfo getNai() {
+            return mNai;
+        }
+
+        public String toString() {
+            return new StringBuffer("KeepaliveInfo [")
+                    .append(" network=").append(mNai.network)
+                    .append(" isStarted=").append(isStarted)
+                    .append(" ")
+                    .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort))
+                    .append("->")
+                    .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
+                    .append(" interval=" + mInterval)
+                    .append(" data=" + HexDump.toHexString(mPacket.data))
+                    .append(" uid=").append(mUid).append(" pid=").append(mPid)
+                    .append(" ]")
+                    .toString();
+        }
+
+        /** Sends a message back to the application via its PacketKeepalive.Callback. */
+        void notifyMessenger(int slot, int err) {
+            KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err);
+        }
+
+        /** Called when the application process is killed. */
+        public void binderDied() {
+            // Not called from ConnectivityService handler thread, so send it a message.
+            mConnectivityServiceHandler.obtainMessage(
+                    NetworkAgent.CMD_STOP_PACKET_KEEPALIVE,
+                    mSlot, PacketKeepalive.BINDER_DIED, mNai.network).sendToTarget();
+        }
+
+        void unlinkDeathRecipient() {
+            if (mBinder != null) {
+                mBinder.unlinkToDeath(this, 0);
+            }
+        }
+
+        private int checkNetworkConnected() {
+            if (!mNai.networkInfo.isConnectedOrConnecting()) {
+                return ERROR_INVALID_NETWORK;
+            }
+            return SUCCESS;
+        }
+
+        private int checkSourceAddress() {
+            // Check that we have the source address.
+            for (InetAddress address : mNai.linkProperties.getAddresses()) {
+                if (address.equals(mPacket.srcAddress)) {
+                    return SUCCESS;
+                }
+            }
+            return ERROR_INVALID_IP_ADDRESS;
+        }
+
+        private int checkInterval() {
+            return mInterval >= 20 ? SUCCESS : ERROR_INVALID_INTERVAL;
+        }
+
+        private int isValid() {
+            synchronized (mNai) {
+                int error = checkInterval();
+                if (error == SUCCESS) error = checkNetworkConnected();
+                if (error == SUCCESS) error = checkSourceAddress();
+                return error;
+            }
+        }
+
+        void start(int slot) {
+            int error = isValid();
+            if (error == SUCCESS) {
+                mSlot = slot;
+                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+                mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket);
+            } else {
+                notifyMessenger(NO_KEEPALIVE, error);
+                return;
+            }
+        }
+
+        void stop(int reason) {
+            int uid = Binder.getCallingUid();
+            if (uid != mUid && uid != Process.SYSTEM_UID) {
+                if (DBG) {
+                    Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
+                }
+            }
+            if (isStarted) {
+                Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
+                mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot);
+            }
+            notifyMessenger(mSlot, reason);
+            unlinkDeathRecipient();
+        }
+    }
+
+    void notifyMessenger(Messenger messenger, int slot, int err) {
+        Message message = Message.obtain();
+        message.what = EVENT_PACKET_KEEPALIVE;
+        message.arg1 = slot;
+        message.arg2 = err;
+        message.obj = null;
+        try {
+            messenger.send(message);
+        } catch (RemoteException e) {
+            // Process died?
+        }
+    }
+
+    private  int findFirstFreeSlot(NetworkAgentInfo nai) {
+        HashMap networkKeepalives = mKeepalives.get(nai);
+        if (networkKeepalives == null) {
+            networkKeepalives = new HashMap<Integer, KeepaliveInfo>();
+            mKeepalives.put(nai, networkKeepalives);
+        }
+
+        // Find the lowest-numbered free slot.
+        int slot;
+        for (slot = 0; slot < networkKeepalives.size(); slot++) {
+            if (networkKeepalives.get(slot) == null) {
+                return slot;
+            }
+        }
+        // No free slot, pick one at the end.
+
+        // HACK for broadcom hardware that does not support slot 0!
+        if (slot == 0) slot = 1;
+        return slot;
+    }
+
+    public void handleStartKeepalive(Message message) {
+        KeepaliveInfo ki = (KeepaliveInfo) message.obj;
+        NetworkAgentInfo nai = ki.getNai();
+        int slot = findFirstFreeSlot(nai);
+        mKeepalives.get(nai).put(slot, ki);
+        ki.start(slot);
+    }
+
+    public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
+        HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+        if (networkKeepalives != null) {
+            for (KeepaliveInfo ki : networkKeepalives.values()) {
+                ki.stop(reason);
+            }
+            networkKeepalives.clear();
+            mKeepalives.remove(nai);
+        }
+    }
+
+    public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
+        HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+        if (networkKeepalives == null) {
+            Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + nai.name());
+            return;
+        }
+        KeepaliveInfo ki = networkKeepalives.get(slot);
+        if (ki == null) {
+            Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + nai.name());
+            return;
+        }
+        ki.stop(reason);
+        networkKeepalives.remove(slot);
+        if (networkKeepalives.isEmpty()) {
+            mKeepalives.remove(nai);
+        }
+    }
+
+    public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
+        HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+        if (networkKeepalives != null) {
+            ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>();
+            for (int slot : networkKeepalives.keySet()) {
+                int error = networkKeepalives.get(slot).isValid();
+                if (error != SUCCESS) {
+                    invalidKeepalives.add(Pair.create(slot, error));
+                }
+            }
+            for (Pair<Integer, Integer> slotAndError: invalidKeepalives) {
+                handleStopKeepalive(nai, slotAndError.first, slotAndError.second);
+            }
+        }
+    }
+
+    public void handleEventPacketKeepalive(NetworkAgentInfo nai, Message message) {
+        int slot = message.arg1;
+        int reason = message.arg2;
+
+        KeepaliveInfo ki = null;
+        try {
+            ki = mKeepalives.get(nai).get(slot);
+        } catch(NullPointerException e) {}
+        if (ki == null) {
+            Log.e(TAG, "Event for unknown keepalive " + slot + " on " + nai.name());
+            return;
+        }
+
+        if (reason == SUCCESS && !ki.isStarted) {
+            // Keepalive successfully started.
+            if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+            ki.isStarted = true;
+            ki.notifyMessenger(slot, reason);
+        } else {
+            // Keepalive successfully stopped, or error.
+            ki.isStarted = false;
+            if (reason == SUCCESS) {
+                if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
+            } else {
+                if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
+            }
+            handleStopKeepalive(nai, slot, reason);
+        }
+    }
+
+    public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger,
+            IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) {
+        InetAddress srcAddress, dstAddress;
+        try {
+            srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
+            dstAddress = NetworkUtils.numericToInetAddress(dstAddrString);
+        } catch (IllegalArgumentException e) {
+            notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_IP_ADDRESS);
+            return;
+        }
+
+        KeepalivePacketData packet;
+        try {
+            packet = KeepalivePacketData.nattKeepalivePacket(
+                    srcAddress, srcPort, dstAddress, NATT_PORT);
+        } catch (KeepalivePacketData.InvalidPacketException e) {
+            notifyMessenger(messenger, NO_KEEPALIVE, e.error);
+            return;
+        }
+        KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds);
+        Log.d(TAG, "Created keepalive: " + ki.toString());
+        mConnectivityServiceHandler.obtainMessage(
+                NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget();
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("Packet keepalives:");
+        pw.increaseIndent();
+        for (NetworkAgentInfo nai : mKeepalives.keySet()) {
+            pw.println(nai.name());
+            pw.increaseIndent();
+            for (int slot : mKeepalives.get(nai).keySet()) {
+                KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
+                pw.println(slot + ": " + ki.toString());
+            }
+            pw.decreaseIndent();
+        }
+        pw.decreaseIndent();
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 39333f6..0029279 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -195,6 +195,12 @@
                 request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities);
     }
 
+    public boolean satisfiesImmutableCapabilitiesOf(NetworkRequest request) {
+        return created &&
+                request.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
+                        networkCapabilities);
+    }
+
     public boolean isVPN() {
         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
     }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 2c9d82b..470bd5a 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -16,11 +16,11 @@
 
 package com.android.server.fingerprint;
 
+import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.IUserSwitchObserver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
@@ -29,8 +29,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
-import android.os.Looper;
-import android.os.MessageQueue;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SELinux;
@@ -40,20 +38,28 @@
 import android.os.UserManager;
 import android.util.Slog;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.server.SystemService;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.fingerprint.IFingerprintDaemon;
 import android.hardware.fingerprint.IFingerprintDaemonCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.view.Display;
 
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
 import java.io.File;
-import java.util.ArrayList;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -250,6 +256,9 @@
             Slog.v(TAG, "Reset fingerprint lockout");
         }
         mFailedAttempts = 0;
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard), the runnable
+        // may still be in the queue; remove it.
+        mHandler.removeCallbacks(mLockoutReset);
     }
 
     private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
@@ -289,6 +298,7 @@
             final int result = daemon.enroll(cryptoToken, groupId, timeout);
             if (result != 0) {
                 Slog.w(TAG, "startEnroll failed, result=" + result);
+                dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "startEnroll failed", e);
@@ -382,6 +392,7 @@
             final int result = daemon.authenticate(opId, groupId);
             if (result != 0) {
                 Slog.w(TAG, "startAuthentication failed, result=" + result);
+                dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "startAuthentication failed", e);
@@ -424,12 +435,14 @@
             return;
         }
 
+        stopPendingOperations(true);
         mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
         // The fingerprint template ids will be removed when we get confirmation from the HAL
         try {
             final int result = daemon.remove(fingerId, userId);
             if (result != 0) {
                 Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
+                dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "startRemove failed", e);
@@ -557,6 +570,7 @@
         private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
             if (receiver == null) return true; // client not listening
             FingerprintUtils.vibrateFingerprintSuccess(getContext());
+            MetricsLogger.action(mContext, MetricsLogger.ACTION_FINGERPRINT_ENROLL);
             try {
                 receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
                 return remaining == 0;
@@ -574,6 +588,8 @@
             boolean authenticated = fpId != 0;
             if (receiver != null) {
                 try {
+                    MetricsLogger.action(mContext, MetricsLogger.ACTION_FINGERPRINT_AUTH,
+                            authenticated);
                     if (!authenticated) {
                         receiver.onAuthenticationFailed(mHalDeviceId);
                     } else {
@@ -589,10 +605,14 @@
                 result = true; // client not listening
 	    }
 	    if (fpId == 0) {
-                FingerprintUtils.vibrateFingerprintError(getContext());
+                if (receiver != null) {
+                    FingerprintUtils.vibrateFingerprintError(getContext());
+                }
                 result |= handleFailedAttempt(this);
             } else {
-                FingerprintUtils.vibrateFingerprintSuccess(getContext());
+                if (receiver != null) {
+                    FingerprintUtils.vibrateFingerprintSuccess(getContext());
+                }
                 result |= true; // we have a valid fingerprint
                 mLockoutReset.run();
             }
@@ -665,7 +685,6 @@
         public void onEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
             dispatchEnumerate(deviceId, fingerIds, groupIds);
         }
-
     };
 
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
@@ -749,6 +768,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
+                    MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
                     startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted);
                 }
             });
@@ -849,6 +869,52 @@
 
             return FingerprintService.this.getAuthenticatorId();
         }
+
+        @Override // Binder call
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump Fingerprint from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                dumpInternal(pw);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+        @Override // Binder call
+        public void resetTimeout(byte [] token) {
+            checkPermission(RESET_FINGERPRINT_LOCKOUT);
+            // TODO: confirm security token when we move timeout management into the HAL layer.
+            mLockoutReset.run();
+        }
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        JSONObject dump = new JSONObject();
+        try {
+            dump.put("service", "Fingerprint Manager");
+
+            JSONArray sets = new JSONArray();
+            for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+                final int userId = user.getUserHandle().getIdentifier();
+                final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
+                JSONObject set = new JSONObject();
+                set.put("id", userId);
+                set.put("count", N);
+                sets.put(set);
+            }
+
+            dump.put("prints", sets);
+        } catch (JSONException e) {
+            Slog.e(TAG, "dump formatting failure", e);
+        }
+        pw.println(dump);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
index d274412..49dc8e4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.Vibrator;
+import android.text.TextUtils;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -64,6 +65,10 @@
     }
 
     public void renameFingerprintForUser(Context ctx, int fingerId, int userId, CharSequence name) {
+        if (TextUtils.isEmpty(name)) {
+            // Don't do the rename if it's empty
+            return;
+        }
         getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
     }
 
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index 607805b..2eb58bf 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -20,8 +20,10 @@
 
 import android.content.Context;
 import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
 import android.hardware.location.IActivityRecognitionHardwareWatcher;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -34,21 +36,24 @@
     private static final String TAG = "ActivityRecognitionProxy";
 
     private final ServiceWatcher mServiceWatcher;
-    private final ActivityRecognitionHardware mActivityRecognitionHardware;
+    private final boolean mIsSupported;
+    private final ActivityRecognitionHardware mInstance;
 
     private ActivityRecognitionProxy(
             Context context,
             Handler handler,
+            boolean activityRecognitionHardwareIsSupported,
             ActivityRecognitionHardware activityRecognitionHardware,
             int overlaySwitchResId,
             int defaultServicePackageNameResId,
             int initialPackageNameResId) {
-        mActivityRecognitionHardware = activityRecognitionHardware;
+        mIsSupported = activityRecognitionHardwareIsSupported;
+        mInstance = activityRecognitionHardware;
 
         Runnable newServiceWork = new Runnable() {
             @Override
             public void run() {
-                bindProvider(mActivityRecognitionHardware);
+                bindProvider();
             }
         };
 
@@ -72,6 +77,7 @@
     public static ActivityRecognitionProxy createAndBind(
             Context context,
             Handler handler,
+            boolean activityRecognitionHardwareIsSupported,
             ActivityRecognitionHardware activityRecognitionHardware,
             int overlaySwitchResId,
             int defaultServicePackageNameResId,
@@ -79,6 +85,7 @@
         ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
                 context,
                 handler,
+                activityRecognitionHardwareIsSupported,
                 activityRecognitionHardware,
                 overlaySwitchResId,
                 defaultServicePackageNameResId,
@@ -89,25 +96,52 @@
             Log.e(TAG, "ServiceWatcher could not start.");
             return null;
         }
-
         return activityRecognitionProxy;
     }
 
     /**
      * Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance.
      */
-    private void bindProvider(ActivityRecognitionHardware activityRecognitionHardware) {
-        IActivityRecognitionHardwareWatcher watcher =
-                IActivityRecognitionHardwareWatcher.Stub.asInterface(mServiceWatcher.getBinder());
-        if (watcher == null) {
-            Log.e(TAG, "No provider instance found on connection.");
+    private void bindProvider() {
+        IBinder binder = mServiceWatcher.getBinder();
+        if (binder == null) {
+            Log.e(TAG, "Null binder found on connection.");
+            return;
+        }
+        String descriptor;
+        try {
+            descriptor = binder.getInterfaceDescriptor();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get interface descriptor.", e);
             return;
         }
 
-        try {
-            watcher.onInstanceChanged(mActivityRecognitionHardware);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error delivering hardware interface.", e);
+        if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
+            IActivityRecognitionHardwareWatcher watcher =
+                    IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+            if (watcher == null) {
+                Log.e(TAG, "No watcher found on connection.");
+                return;
+            }
+            try {
+                watcher.onInstanceChanged(mInstance);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error delivering hardware interface to watcher.", e);
+            }
+        } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
+            IActivityRecognitionHardwareClient client =
+                    IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+            if (client == null) {
+                Log.e(TAG, "No client found on connection.");
+                return;
+            }
+            try {
+                client.onAvailabilityChanged(mIsSupported, mInstance);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error delivering hardware interface to client.", e);
+            }
+        } else {
+            Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index ba5f516..02f2c73 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -766,6 +766,10 @@
                 && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
             if (mNetworkAvailable) {
                 String apnName = info.getExtraInfo();
+                // APN wasn't found in the intent, try to get it from the content provider.
+                if (apnName == null) {
+                    apnName = getSelectedApn();
+                }
                 if (apnName == null) {
                     /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
                     exception in the following call to native_agps_data_conn_open*/
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5c1878e..8845d6e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -108,6 +108,7 @@
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
@@ -201,6 +202,8 @@
     private static final int VERSION_LATEST = VERSION_SWITCH_UID;
 
     @VisibleForTesting
+    public static final int TYPE_NONE = 0;
+    @VisibleForTesting
     public static final int TYPE_WARNING = 0x1;
     @VisibleForTesting
     public static final int TYPE_LIMIT = 0x2;
@@ -260,6 +263,9 @@
     private PowerManagerInternal mPowerManagerInternal;
     private IDeviceIdleController mDeviceIdleController;
 
+    private final ComponentName mNotificationComponent;
+    private int mNotificationSequenceNumber;
+
     final Object mRulesLock = new Object();
 
     volatile boolean mSystemReady;
@@ -357,6 +363,11 @@
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
 
         mAppOps = context.getSystemService(AppOpsManager.class);
+
+        final String notificationComponent = context.getString(
+                R.string.config_networkPolicyNotificationComponent);
+        mNotificationComponent = notificationComponent != null
+                ? ComponentName.unflattenFromString(notificationComponent) : null;
     }
 
     public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -778,6 +789,11 @@
         final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
         mActiveNotifs.clear();
 
+        // increment the sequence number so custom components know
+        // this update is new
+        mNotificationSequenceNumber++;
+        boolean hasNotifications = false;
+
         // TODO: when switching to kernel notifications, compute next future
         // cycle boundary to recompute notifications.
 
@@ -794,6 +810,7 @@
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (policy.isOverLimit(totalBytes)) {
+                hasNotifications = true;
                 if (policy.lastLimitSnooze >= start) {
                     enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
                 } else {
@@ -806,10 +823,18 @@
 
                 if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
                     enqueueNotification(policy, TYPE_WARNING, totalBytes);
+                    hasNotifications = true;
                 }
             }
         }
 
+        // right now we don't care about restricted background notifications
+        // in the custom notification component, so trigger an update now
+        // if we didn't update anything this pass
+        if (!hasNotifications) {
+            sendNotificationToCustomComponent(null, TYPE_NONE, 0);
+        }
+
         // ongoing notification when restricting background data
         if (mRestrictBackground) {
             enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
@@ -856,6 +881,12 @@
      * {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
      */
     private void notifyOverLimitLocked(NetworkTemplate template) {
+        if (mNotificationComponent != null) {
+            // It is the job of the notification component to handle UI,
+            // so we do nothing here
+            return;
+        }
+
         if (!mOverLimitNotified.contains(template)) {
             mContext.startActivity(buildNetworkOverLimitIntent(template));
             mOverLimitNotified.add(template);
@@ -874,11 +905,55 @@
         return TAG + ":" + policy.template.hashCode() + ":" + type;
     }
 
+    private boolean sendNotificationToCustomComponent(
+            NetworkPolicy policy,
+            int type,
+            long totalBytes) {
+        if (mNotificationComponent == null) {
+            return false;
+        }
+
+        Intent intent = new Intent();
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(mNotificationComponent);
+
+        int notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_NONE;
+        switch (type) {
+            case TYPE_WARNING:
+                notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_WARNING;
+                break;
+            case TYPE_LIMIT:
+                notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_REACHED_LIMIT;
+                break;
+            case TYPE_LIMIT_SNOOZED:
+                notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT;
+                break;
+        }
+
+        intent.setAction(NetworkPolicyManager.ACTION_SHOW_NETWORK_POLICY_NOTIFICATION);
+        intent.putExtra(NetworkPolicyManager.EXTRA_NOTIFICATION_TYPE, notificationType);
+        intent.putExtra(
+                NetworkPolicyManager.EXTRA_NOTIFICATION_SEQUENCE_NUMBER,
+                mNotificationSequenceNumber);
+
+        if (notificationType != NetworkPolicyManager.NOTIFICATION_TYPE_NONE) {
+            intent.putExtra(NetworkPolicyManager.EXTRA_NETWORK_POLICY, policy);
+            intent.putExtra(NetworkPolicyManager.EXTRA_BYTES_USED, totalBytes);
+        }
+
+        mContext.sendBroadcast(intent);
+        return true;
+    }
+
     /**
      * Show notification for combined {@link NetworkPolicy} and specific type,
      * like {@link #TYPE_LIMIT}. Okay to call multiple times.
      */
     private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) {
+        if (sendNotificationToCustomComponent(policy, type, totalBytes)) {
+            return;
+        }
+
         final String tag = buildNotificationTag(policy, type);
         final Notification.Builder builder = new Notification.Builder(mContext);
         builder.setOnlyAlertOnce(true);
@@ -1738,6 +1813,19 @@
         }
     }
 
+    @Override
+    public void snoozeWarning(NetworkTemplate template) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // TODO: this seems like a race condition? (along with snoozeLimit above)
+            performSnooze(template, TYPE_WARNING);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     void performSnooze(NetworkTemplate template, int type) {
         maybeRefreshTrustedTime();
         final long currentTime = currentTimeMillis();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 57d7758..2fda745 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -411,8 +411,7 @@
         applyRestrictions(muteNotifications, USAGE_NOTIFICATION);
 
         // call restrictions
-        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
-                || mEffectsSuppressed;
+        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers;
         applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE);
 
         // alarm restrictions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f67f3f3..e1bd9f9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4626,7 +4626,7 @@
             if (result == null) {
                 result = new CrossProfileDomainInfo();
                 result.resolveInfo =
-                        createForwardingResolveInfo(null, sourceUserId, parentUserId);
+                        createForwardingResolveInfo(new IntentFilter(), sourceUserId, parentUserId);
                 result.bestDomainVerificationStatus = status;
             } else {
                 result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
@@ -15774,7 +15774,7 @@
             if (userDir.exists()) continue;
 
             try {
-                UserManagerService.prepareUserDirectory(userDir);
+                UserManagerService.prepareUserDirectory(mContext, volumeUuid, user.id);
                 UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
             } catch (IOException e) {
                 Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a762014..943e649 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -38,10 +38,13 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.IBinder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PatternMatcher;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0607525..06c3682 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1285,7 +1285,7 @@
                         try {
                             final File userDir = Environment.getDataUserDirectory(volumeUuid,
                                     userId);
-                            prepareUserDirectory(userDir);
+                            prepareUserDirectory(mContext, volumeUuid, userId);
                             enforceSerialNumber(userDir, userInfo.serialNumber);
                         } catch (IOException e) {
                             Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -1493,6 +1493,8 @@
     }
 
     private void removeUserStateLocked(final int userHandle) {
+        mContext.getSystemService(StorageManager.class)
+            .deleteUserKey(userHandle);
         // Cleanup package manager settings
         mPm.cleanUpUserLILPw(this, userHandle);
 
@@ -1899,16 +1901,10 @@
      * Create new {@code /data/user/[id]} directory and sets default
      * permissions.
      */
-    public static void prepareUserDirectory(File file) throws IOException {
-        if (!file.exists()) {
-            if (!file.mkdir()) {
-                throw new IOException("Failed to create " + file);
-            }
-        }
-        if (FileUtils.setPermissions(file.getAbsolutePath(), 0771, Process.SYSTEM_UID,
-                Process.SYSTEM_UID) != 0) {
-            throw new IOException("Failed to prepare " + file);
-        }
+    public static void prepareUserDirectory(Context context, String volumeUuid, int userId) {
+        final StorageManager storage = context.getSystemService(StorageManager.class);
+        final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
+        storage.createNewUserDir(userId, userDir);
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
index fef1e575..e6ec6a6 100644
--- a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
+++ b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
@@ -72,6 +72,9 @@
     /* 1 means increasing, -1 means decreasing */
     private int mYOffsetDirection = 1;
 
+    private int mAppliedBurnInXOffset = 0;
+    private int mAppliedBurnInYOffset = 0;
+
     private final AlarmManager mAlarmManager;
     private final PendingIntent mBurnInProtectionIntent;
     private final DisplayManagerInternal mDisplayManagerInternal;
@@ -139,6 +142,8 @@
                 mFirstUpdate = false;
             } else {
                 adjustOffsets();
+                mAppliedBurnInXOffset = mLastBurnInXOffset;
+                mAppliedBurnInYOffset = mLastBurnInYOffset;
                 mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
                         mLastBurnInXOffset, mLastBurnInYOffset);
             }
@@ -258,6 +263,8 @@
     @Override
     public void onAnimationEnd(Animator animator) {
         if (animator == mCenteringAnimator && !mBurnInProtectionActive) {
+            mAppliedBurnInXOffset = 0;
+            mAppliedBurnInYOffset = 0;
             // No matter how the animation finishes, we want to zero the offsets.
             mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(), 0, 0);
         }
@@ -276,7 +283,7 @@
         if (!mBurnInProtectionActive) {
             final float value = (Float) valueAnimator.getAnimatedValue();
             mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
-                    (int) (mLastBurnInXOffset * value), (int) (mLastBurnInYOffset * value));
+                    (int) (mAppliedBurnInXOffset * value), (int) (mAppliedBurnInYOffset * value));
         }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 489bcdb..0423aa3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4410,7 +4410,7 @@
                     if (mAppsToBeHidden.isEmpty()) {
                         if (dismissKeyguard && !mKeyguardSecure) {
                             mAppsThatDismissKeyguard.add(appToken);
-                        } else {
+                        } else if (win.isDrawnLw()) {
                             mWinShowWhenLocked = win;
                             mHideLockScreen = true;
                             mForceStatusBarFromKeyguard = false;
@@ -4444,7 +4444,7 @@
                         mWinDismissingKeyguard = win;
                         mSecureDismissingKeyguard = mKeyguardSecure;
                         mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
-                    } else if (mAppsToBeHidden.isEmpty() && showWhenLocked) {
+                    } else if (mAppsToBeHidden.isEmpty() && showWhenLocked && win.isDrawnLw()) {
                         if (DEBUG_LAYOUT) Slog.v(TAG,
                                 "Setting mHideLockScreen to true by win " + win);
                         mHideLockScreen = true;
@@ -6085,6 +6085,7 @@
         }
         startedWakingUp();
         screenTurningOn(null);
+        screenTurnedOn();
     }
 
     ProgressDialog mBootMsgDialog = null;
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index d1b50da..b1ae922 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -72,7 +72,9 @@
                         if (statusbar != null) {
                             long startTime = calculateStatusBarTransitionStartTime(openAnimation,
                                     closeAnimation);
-                            statusbar.appTransitionStarting(startTime, TRANSITION_DURATION);
+                            long duration = closeAnimation != null || openAnimation != null
+                                    ? TRANSITION_DURATION : 0;
+                            statusbar.appTransitionStarting(startTime, duration);
                         }
                     } catch (RemoteException e) {
                         Slog.e(mTag, "RemoteException when app transition is starting", e);
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index c71b48f..9916223 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -24,10 +24,12 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * A special helper class used by the WindowManager
@@ -52,8 +54,9 @@
     private SensorManager mSensorManager;
     private boolean mEnabled;
     private int mRate;
+    private String mSensorType;
     private Sensor mSensor;
-    private SensorEventListenerImpl mSensorEventListener;
+    private OrientationJudge mOrientationJudge;
     private int mCurrentRotation = -1;
 
     private final Object mLock = new Object();
@@ -67,7 +70,7 @@
     public WindowOrientationListener(Context context, Handler handler) {
         this(context, handler, SensorManager.SENSOR_DELAY_UI);
     }
-    
+
     /**
      * Creates a new WindowOrientationListener.
      * 
@@ -84,11 +87,31 @@
         mHandler = handler;
         mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
         mRate = rate;
-        mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
-                ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
-        if (mSensor != null) {
-            // Create listener only if sensors do exist
-            mSensorEventListener = new SensorEventListenerImpl(context);
+
+        mSensorType = context.getResources().getString(
+                com.android.internal.R.string.config_orientationSensorType);
+        if (!TextUtils.isEmpty(mSensorType)) {
+            List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+            final int N = sensors.size();
+            for (int i = 0; i < N; i++) {
+                Sensor sensor = sensors.get(i);
+                if (mSensorType.equals(sensor.getStringType())) {
+                    mSensor = sensor;
+                    break;
+                }
+            }
+            if (mSensor != null) {
+                mOrientationJudge = new OrientationSensorJudge();
+            }
+        }
+
+        if (mOrientationJudge == null) {
+            mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
+                    ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
+            if (mSensor != null) {
+                // Create listener only if sensors do exist
+                mOrientationJudge = new AccelSensorJudge(context);
+            }
         }
     }
 
@@ -106,8 +129,8 @@
                 if (LOG) {
                     Slog.d(TAG, "WindowOrientationListener enabled");
                 }
-                mSensorEventListener.resetLocked();
-                mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);
+                mOrientationJudge.resetLocked();
+                mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
                 mEnabled = true;
             }
         }
@@ -126,7 +149,7 @@
                 if (LOG) {
                     Slog.d(TAG, "WindowOrientationListener disabled");
                 }
-                mSensorManager.unregisterListener(mSensorEventListener);
+                mSensorManager.unregisterListener(mOrientationJudge);
                 mEnabled = false;
             }
         }
@@ -134,8 +157,8 @@
 
     public void onTouchStart() {
         synchronized (mLock) {
-            if (mSensorEventListener != null) {
-                mSensorEventListener.onTouchStartLocked();
+            if (mOrientationJudge != null) {
+                mOrientationJudge.onTouchStartLocked();
             }
         }
     }
@@ -144,8 +167,8 @@
         long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
 
         synchronized (mLock) {
-            if (mSensorEventListener != null) {
-                mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+            if (mOrientationJudge != null) {
+                mOrientationJudge.onTouchEndLocked(whenElapsedNanos);
             }
         }
     }
@@ -172,7 +195,7 @@
     public int getProposedRotation() {
         synchronized (mLock) {
             if (mEnabled) {
-                return mSensorEventListener.getProposedRotationLocked();
+                return mOrientationJudge.getProposedRotationLocked();
             }
             return -1;
         }
@@ -194,6 +217,8 @@
      * It is called each time the orientation determination transitions from being
      * uncertain to being certain again, even if it is the same orientation as before.
      *
+     * This should only be called on the Handler thread.
+     *
      * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
      * @see android.view.Surface
      */
@@ -205,15 +230,77 @@
             prefix += "  ";
             pw.println(prefix + "mEnabled=" + mEnabled);
             pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
+            pw.println(prefix + "mSensorType=" + mSensorType);
             pw.println(prefix + "mSensor=" + mSensor);
             pw.println(prefix + "mRate=" + mRate);
 
-            if (mSensorEventListener != null) {
-                mSensorEventListener.dumpLocked(pw, prefix);
+            if (mOrientationJudge != null) {
+                mOrientationJudge.dumpLocked(pw, prefix);
             }
         }
     }
 
+    abstract class OrientationJudge implements SensorEventListener {
+        // Number of nanoseconds per millisecond.
+        protected static final long NANOS_PER_MS = 1000000;
+
+        // Number of milliseconds per nano second.
+        protected static final float MILLIS_PER_NANO = 0.000001f;
+
+        // The minimum amount of time that must have elapsed since the screen was last touched
+        // before the proposed rotation can change.
+        protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
+                500 * NANOS_PER_MS;
+
+        /**
+         * Gets the proposed rotation.
+         *
+         * This method only returns a rotation if the orientation listener is certain
+         * of its proposal.  If the rotation is indeterminate, returns -1.
+         *
+         * Should only be called when holding WindowOrientationListener lock.
+         *
+         * @return The proposed rotation, or -1 if unknown.
+         */
+        public abstract int getProposedRotationLocked();
+
+        /**
+         * Notifies the orientation judge that the screen is being touched.
+         *
+         * Should only be called when holding WindowOrientationListener lock.
+         */
+        public abstract void onTouchStartLocked();
+
+        /**
+         * Notifies the orientation judge that the screen is no longer being touched.
+         *
+         * Should only be called when holding WindowOrientationListener lock.
+         *
+         * @param whenElapsedNanos Given in the elapsed realtime nanos time base.
+         */
+        public abstract void onTouchEndLocked(long whenElapsedNanos);
+
+        /**
+         * Resets the state of the judge.
+         *
+         * Should only be called when holding WindowOrientationListener lock.
+         */
+        public abstract void resetLocked();
+
+        /**
+         * Dumps internal state of the orientation judge.
+         *
+         * Should only be called when holding WindowOrientationListener lock.
+         */
+        public abstract void dumpLocked(PrintWriter pw, String prefix);
+
+        @Override
+        public abstract void onAccuracyChanged(Sensor sensor, int accuracy);
+
+        @Override
+        public abstract void onSensorChanged(SensorEvent event);
+    }
+
     /**
      * This class filters the raw accelerometer data and tries to detect actual changes in
      * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
@@ -252,13 +339,10 @@
      * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
      * signal processing background.
      */
-    final class SensorEventListenerImpl implements SensorEventListener {
+    final class AccelSensorJudge extends OrientationJudge {
         // We work with all angles in degrees in this class.
         private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
 
-        // Number of nanoseconds per millisecond.
-        private static final long NANOS_PER_MS = 1000000;
-
         // Indices into SensorEvent.values for the accelerometer sensor.
         private static final int ACCELEROMETER_DATA_X = 0;
         private static final int ACCELEROMETER_DATA_Y = 1;
@@ -286,11 +370,6 @@
         private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
                 500 * NANOS_PER_MS;
 
-        // The minimum amount of time that must have elapsed since the screen was last touched
-        // before the proposed rotation can change.
-        private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
-                500 * NANOS_PER_MS;
-
         // If the tilt angle remains greater than the specified angle for a minimum of
         // the specified time, then the device is deemed to be lying flat
         // (just chillin' on a table).
@@ -434,7 +513,7 @@
         private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
         private int mTiltHistoryIndex;
 
-        public SensorEventListenerImpl(Context context) {
+        public AccelSensorJudge(Context context) {
             // Load tilt tolerance configuration.
             int[] tiltTolerance = context.getResources().getIntArray(
                     com.android.internal.R.array.config_autoRotationTiltTolerance);
@@ -455,11 +534,15 @@
             }
         }
 
+        @Override
         public int getProposedRotationLocked() {
             return mProposedRotation;
         }
 
+        @Override
         public void dumpLocked(PrintWriter pw, String prefix) {
+            pw.println(prefix + "AccelSensorJudge");
+            prefix += "  ";
             pw.println(prefix + "mProposedRotation=" + mProposedRotation);
             pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
             pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
@@ -689,6 +772,33 @@
             }
         }
 
+        @Override
+        public void onTouchStartLocked() {
+            mTouched = true;
+        }
+
+        @Override
+        public void onTouchEndLocked(long whenElapsedNanos) {
+            mTouched = false;
+            mTouchEndedTimestampNanos = whenElapsedNanos;
+        }
+
+        @Override
+        public void resetLocked() {
+            mLastFilteredTimestampNanos = Long.MIN_VALUE;
+            mProposedRotation = -1;
+            mFlatTimestampNanos = Long.MIN_VALUE;
+            mFlat = false;
+            mSwingTimestampNanos = Long.MIN_VALUE;
+            mSwinging = false;
+            mAccelerationTimestampNanos = Long.MIN_VALUE;
+            mAccelerating = false;
+            mOverhead = false;
+            clearPredictedRotationLocked();
+            clearTiltHistoryLocked();
+        }
+
+
         /**
          * Returns true if the tilt angle is acceptable for a given predicted rotation.
          */
@@ -787,20 +897,6 @@
             return true;
         }
 
-        private void resetLocked() {
-            mLastFilteredTimestampNanos = Long.MIN_VALUE;
-            mProposedRotation = -1;
-            mFlatTimestampNanos = Long.MIN_VALUE;
-            mFlat = false;
-            mSwingTimestampNanos = Long.MIN_VALUE;
-            mSwinging = false;
-            mAccelerationTimestampNanos = Long.MIN_VALUE;
-            mAccelerating = false;
-            mOverhead = false;
-            clearPredictedRotationLocked();
-            clearTiltHistoryLocked();
-        }
-
         private void clearPredictedRotationLocked() {
             mPredictedRotation = -1;
             mPredictedRotationTimestampNanos = Long.MIN_VALUE;
@@ -869,14 +965,147 @@
         private float remainingMS(long now, long until) {
             return now >= until ? 0 : (until - now) * 0.000001f;
         }
+    }
 
-        private void onTouchStartLocked() {
-            mTouched = true;
+    final class OrientationSensorJudge extends OrientationJudge {
+        private boolean mTouching;
+        private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
+        private int mProposedRotation = -1;
+        private int mDesiredRotation = -1;
+        private boolean mRotationEvaluationScheduled;
+
+        @Override
+        public int getProposedRotationLocked() {
+            return mProposedRotation;
         }
 
-        private void onTouchEndLocked(long whenElapsedNanos) {
-            mTouched = false;
+        @Override
+        public void onTouchStartLocked() {
+            mTouching = true;
+        }
+
+        @Override
+        public void onTouchEndLocked(long whenElapsedNanos) {
+            mTouching = false;
             mTouchEndedTimestampNanos = whenElapsedNanos;
+            if (mDesiredRotation != mProposedRotation) {
+                final long now = SystemClock.elapsedRealtimeNanos();
+                scheduleRotationEvaluationIfNecessaryLocked(now);
+            }
         }
+
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            int newRotation;
+            synchronized (mLock) {
+                mDesiredRotation = (int) event.values[0];
+                newRotation = evaluateRotationChangeLocked();
+            }
+            if (newRotation >=0) {
+                onProposedRotationChanged(newRotation);
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) { }
+
+        @Override
+        public void dumpLocked(PrintWriter pw, String prefix) {
+            pw.println(prefix + "OrientationSensorJudge");
+            prefix += "  ";
+            pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
+            pw.println(prefix + "mProposedRotation=" + mProposedRotation);
+            pw.println(prefix + "mTouching=" + mTouching);
+            pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
+        }
+
+        @Override
+        public void resetLocked() {
+            mProposedRotation = -1;
+            mDesiredRotation = -1;
+            mTouching = false;
+            mTouchEndedTimestampNanos = Long.MIN_VALUE;
+            unscheduleRotationEvaluationLocked();
+        }
+
+        public int evaluateRotationChangeLocked() {
+            unscheduleRotationEvaluationLocked();
+            if (mDesiredRotation == mProposedRotation) {
+                return -1;
+            }
+            final long now = SystemClock.elapsedRealtimeNanos();
+            if (isDesiredRotationAcceptableLocked(now)) {
+                mProposedRotation = mDesiredRotation;
+                return mProposedRotation;
+            } else {
+                scheduleRotationEvaluationIfNecessaryLocked(now);
+            }
+            return -1;
+        }
+
+        private boolean isDesiredRotationAcceptableLocked(long now) {
+            if (mTouching) {
+                return false;
+            }
+            if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
+                return false;
+            }
+            return true;
+        }
+
+        private void scheduleRotationEvaluationIfNecessaryLocked(long now) {
+            if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) {
+                if (LOG) {
+                    Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
+                            "ignoring, an evaluation is already scheduled or is unnecessary.");
+                }
+                return;
+            }
+            if (mTouching) {
+                if (LOG) {
+                    Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
+                            "ignoring, user is still touching the screen.");
+                }
+                return;
+            }
+            long timeOfNextPossibleRotationNanos =
+                mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS;
+            if (now >= timeOfNextPossibleRotationNanos) {
+                if (LOG) {
+                    Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
+                            "ignoring, already past the next possible time of rotation.");
+                }
+                return;
+            }
+            // Use a delay instead of an absolute time since handlers are in uptime millis and we
+            // use elapsed realtime.
+            final long delayMs =
+                    (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO);
+            mHandler.postDelayed(mRotationEvaluator, delayMs);
+            mRotationEvaluationScheduled = true;
+        }
+
+        private void unscheduleRotationEvaluationLocked() {
+            if (!mRotationEvaluationScheduled) {
+                return;
+            }
+            mHandler.removeCallbacks(mRotationEvaluator);
+            mRotationEvaluationScheduled = false;
+        }
+
+        private Runnable mRotationEvaluator = new Runnable() {
+            @Override
+            public void run() {
+                int newRotation;
+                synchronized (mLock) {
+                    mRotationEvaluationScheduled = false;
+                    newRotation = evaluateRotationChangeLocked();
+                }
+                if (newRotation >= 0) {
+                    onProposedRotationChanged(newRotation);
+                }
+            }
+        };
     }
 }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 5d52307..6b45941 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -32,6 +32,14 @@
     private static final String TAG = "KeyguardServiceDelegate";
     private static final boolean DEBUG = true;
 
+    private static final int SCREEN_STATE_OFF = 0;
+    private static final int SCREEN_STATE_TURNING_ON = 1;
+    private static final int SCREEN_STATE_ON = 2;
+
+    private static final int INTERACTIVE_STATE_SLEEP = 0;
+    private static final int INTERACTIVE_STATE_AWAKE = 1;
+    private static final int INTERACTIVE_STATE_GOING_TO_SLEEP = 2;
+
     protected KeyguardServiceWrapper mKeyguardService;
     private final Context mContext;
     private final View mScrim; // shown if keyguard crashes
@@ -61,6 +69,8 @@
         public int offReason;
         public int currentUser;
         public boolean bootCompleted;
+        public int screenState;
+        public int interactiveState;
     };
 
     public interface DrawnListener {
@@ -144,10 +154,17 @@
                 // If the system is ready, it means keyguard crashed and restarted.
                 mKeyguardService.onSystemReady();
                 // This is used to hide the scrim once keyguard displays.
-                mKeyguardService.onStartedWakingUp();
-                mKeyguardService.onScreenTurningOn(
-                        new KeyguardShowDelegate(mDrawnListenerWhenConnect));
-                mKeyguardService.onScreenTurnedOn();
+                if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
+                    mKeyguardService.onStartedWakingUp();
+                }
+                if (mKeyguardState.screenState == SCREEN_STATE_ON
+                        || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
+                    mKeyguardService.onScreenTurningOn(
+                            new KeyguardShowDelegate(mDrawnListenerWhenConnect));
+                }
+                if (mKeyguardState.screenState == SCREEN_STATE_ON) {
+                    mKeyguardService.onScreenTurnedOn();
+                }
                 mDrawnListenerWhenConnect = null;
             }
             if (mKeyguardState.bootCompleted) {
@@ -231,6 +248,7 @@
             if (DEBUG) Log.v(TAG, "onStartedWakingUp()");
             mKeyguardService.onStartedWakingUp();
         }
+        mKeyguardState.interactiveState = INTERACTIVE_STATE_AWAKE;
     }
 
     public void onScreenTurnedOff() {
@@ -238,6 +256,7 @@
             if (DEBUG) Log.v(TAG, "onScreenTurnedOff()");
             mKeyguardService.onScreenTurnedOff();
         }
+        mKeyguardState.screenState = SCREEN_STATE_OFF;
     }
 
     public void onScreenTurningOn(final DrawnListener drawnListener) {
@@ -252,6 +271,7 @@
             mDrawnListenerWhenConnect = drawnListener;
             showScrim();
         }
+        mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
     }
 
     public void onScreenTurnedOn() {
@@ -259,6 +279,7 @@
             if (DEBUG) Log.v(TAG, "onScreenTurnedOn()");
             mKeyguardService.onScreenTurnedOn();
         }
+        mKeyguardState.screenState = SCREEN_STATE_ON;
     }
 
     public void onStartedGoingToSleep(int why) {
@@ -266,12 +287,14 @@
             mKeyguardService.onStartedGoingToSleep(why);
         }
         mKeyguardState.offReason = why;
+        mKeyguardState.interactiveState = INTERACTIVE_STATE_GOING_TO_SLEEP;
     }
 
     public void onFinishedGoingToSleep(int why) {
         if (mKeyguardService != null) {
             mKeyguardService.onFinishedGoingToSleep(why);
         }
+        mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
     }
 
     public void setKeyguardEnabled(boolean enabled) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 130815e..5d01931 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,4 +28,5 @@
     void showScreenPinningRequest();
     void showAssistDisclosure();
     void startAssist(Bundle args);
+    void onCameraLaunchGestureDetected();
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2a817ea..0fb1169 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -176,6 +176,16 @@
                 }
             }
         }
+
+        @Override
+        public void onCameraLaunchGestureDetected() {
+            if (mBar != null) {
+                try {
+                    mBar.onCameraLaunchGestureDetected();
+                } catch (RemoteException e) {
+                }
+            }
+        }
     };
 
     // ================================================================================
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 15da829..6c70fe9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -19,6 +19,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
 import com.android.server.SystemService;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -59,6 +60,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.Xml;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
@@ -96,16 +98,15 @@
     private static final int MSG_UNREGISTER_LISTENER = 2;
     private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
     private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
-    private static final int MSG_REQUIRE_CREDENTIAL_ENTRY = 5;
     private static final int MSG_KEYGUARD_SHOWING_CHANGED = 6;
     private static final int MSG_START_USER = 7;
     private static final int MSG_CLEANUP_USER = 8;
     private static final int MSG_SWITCH_USER = 9;
 
-    private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
-    private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
+    private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
+    private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
     private final Receiver mReceiver = new Receiver();
-    private final SparseBooleanArray mUserHasAuthenticated = new SparseBooleanArray();
+
     /* package */ final TrustArchive mArchive = new TrustArchive();
     private final Context mContext;
     private final LockPatternUtils mLockPatternUtils;
@@ -118,9 +119,6 @@
     @GuardedBy("mDeviceLockedForUser")
     private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray();
 
-    @GuardedBy("mUserHasAuthenticatedSinceBoot")
-    private final SparseBooleanArray mUserHasAuthenticatedSinceBoot = new SparseBooleanArray();
-
     private boolean mTrustAgentsCanRun = false;
     private int mCurrentUser = UserHandle.USER_OWNER;
 
@@ -146,6 +144,7 @@
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
             mReceiver.register(mContext);
+            mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             mTrustAgentsCanRun = true;
             refreshAgentList(UserHandle.USER_ALL);
@@ -230,7 +229,7 @@
             if (!userInfo.supportsSwitchTo()) continue;
             if (!mActivityManager.isUserRunning(userInfo.id)) continue;
             if (!lockPatternUtils.isSecure(userInfo.id)) continue;
-            if (!getUserHasAuthenticated(userInfo.id)) continue;
+            if (!mStrongAuthTracker.isTrustAllowedForUser(userInfo.id)) continue;
             DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
             int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
             final boolean disableTrustAgents =
@@ -509,7 +508,7 @@
     // Agent dispatch and aggregation
 
     private boolean aggregateIsTrusted(int userId) {
-        if (!getUserHasAuthenticated(userId)) {
+        if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -524,7 +523,7 @@
     }
 
     private boolean aggregateIsTrustManaged(int userId) {
-        if (!getUserHasAuthenticated(userId)) {
+        if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -545,54 +544,6 @@
                 info.agent.onUnlockAttempt(successful);
             }
         }
-
-        if (successful) {
-            updateUserHasAuthenticated(userId);
-        }
-    }
-
-    private void updateUserHasAuthenticated(int userId) {
-        boolean changed = setUserHasAuthenticated(userId);
-        if (changed) {
-            refreshAgentList(userId);
-        }
-    }
-
-    private boolean getUserHasAuthenticated(int userId) {
-        return mUserHasAuthenticated.get(userId);
-    }
-
-    /**
-     * @return whether the value has changed
-     */
-    private boolean setUserHasAuthenticated(int userId) {
-        if (!mUserHasAuthenticated.get(userId)) {
-            mUserHasAuthenticated.put(userId, true);
-            synchronized (mUserHasAuthenticatedSinceBoot) {
-                mUserHasAuthenticatedSinceBoot.put(userId, true);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    private void clearUserHasAuthenticated(int userId) {
-        if (userId == UserHandle.USER_ALL) {
-            mUserHasAuthenticated.clear();
-        } else {
-            mUserHasAuthenticated.put(userId, false);
-        }
-    }
-
-    private boolean getUserHasAuthenticatedSinceBoot(int userId) {
-        synchronized (mUserHasAuthenticatedSinceBoot) {
-            return mUserHasAuthenticatedSinceBoot.get(userId);
-        }
-    }
-
-    private void requireCredentialEntry(int userId) {
-        clearUserHasAuthenticated(userId);
-        refreshAgentList(userId);
     }
 
     // Listeners
@@ -681,17 +632,6 @@
         }
 
         @Override
-        public void reportRequireCredentialEntry(int userId) throws RemoteException {
-            enforceReportPermission();
-            if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
-                mHandler.obtainMessage(MSG_REQUIRE_CREDENTIAL_ENTRY, userId, 0).sendToTarget();
-            } else {
-                throw new IllegalArgumentException(
-                        "userId must be an explicit user id or USER_ALL");
-            }
-        }
-
-        @Override
         public void reportKeyguardShowingChanged() throws RemoteException {
             enforceReportPermission();
             // coalesce refresh messages.
@@ -734,18 +674,6 @@
             }
         }
 
-        @Override
-        public boolean hasUserAuthenticatedSinceBoot(int userId) throws RemoteException {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, null);
-            long token = Binder.clearCallingIdentity();
-            try {
-                return getUserHasAuthenticatedSinceBoot(userId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
         private void enforceReportPermission() {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
@@ -794,9 +722,8 @@
             fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id)));
             fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id)));
             fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id)));
-            fout.print(", hasAuthenticated=" + dumpBool(getUserHasAuthenticated(user.id)));
-            fout.print(", hasAuthenticatedSinceBoot="
-                    + dumpBool(getUserHasAuthenticatedSinceBoot(user.id)));
+            fout.print(", strongAuthRequired=" + dumpHex(
+                    mStrongAuthTracker.getStrongAuthForUser(user.id)));
             fout.println();
             fout.println("   Enabled agents:");
             boolean duplicateSimpleNames = false;
@@ -831,6 +758,10 @@
         private String dumpBool(boolean b) {
             return b ? "1" : "0";
         }
+
+        private String dumpHex(int i) {
+            return "0x" + Integer.toHexString(i);
+        }
     };
 
     private int resolveProfileParent(int userId) {
@@ -864,9 +795,6 @@
                     // This is also called when the security mode of a user changes.
                     refreshDeviceLockedForUser(UserHandle.USER_ALL);
                     break;
-                case MSG_REQUIRE_CREDENTIAL_ENTRY:
-                    requireCredentialEntry(msg.arg1);
-                    break;
                 case MSG_KEYGUARD_SHOWING_CHANGED:
                     refreshDeviceLockedForUser(mCurrentUser);
                     break;
@@ -900,6 +828,13 @@
         }
     };
 
+    private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker() {
+        @Override
+        public void onStrongAuthRequiredChanged(int userId) {
+            refreshAgentList(userId);
+        }
+    };
+
     private class Receiver extends BroadcastReceiver {
 
         @Override
@@ -908,8 +843,6 @@
             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
                 refreshAgentList(getSendingUserId());
                 updateDevicePolicyFeatures();
-            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                updateUserHasAuthenticated(getSendingUserId());
             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
                 int userId = getUserId(intent);
                 if (userId > 0) {
@@ -918,7 +851,6 @@
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 int userId = getUserId(intent);
                 if (userId > 0) {
-                    mUserHasAuthenticated.delete(userId);
                     synchronized (mUserIsTrusted) {
                         mUserIsTrusted.delete(userId);
                     }
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 01f878c..80526f2 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -79,17 +79,20 @@
         }
     }
 
-    public void setOverscanLocked(String name, int left, int top, int right, int bottom) {
+    public void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
+            int bottom) {
         if (left == 0 && top == 0 && right == 0 && bottom == 0) {
             // Right now all we are storing is overscan; if there is no overscan,
             // we have no need for the entry.
+            mEntries.remove(uniqueId);
+            // Legacy name might have been in used, so we need to clear it.
             mEntries.remove(name);
             return;
         }
-        Entry entry = mEntries.get(name);
+        Entry entry = mEntries.get(uniqueId);
         if (entry == null) {
-            entry = new Entry(name);
-            mEntries.put(name, entry);
+            entry = new Entry(uniqueId);
+            mEntries.put(uniqueId, entry);
         }
         entry.overscanLeft = left;
         entry.overscanTop = top;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 794b49c..3da9b44 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -389,7 +389,9 @@
 
     void resetAnimationBackgroundAnimator() {
         mAnimationBackgroundAnimator = null;
-        mAnimationBackgroundSurface.hide();
+        if (mAnimationBackgroundSurface != null) {
+            mAnimationBackgroundSurface.hide();
+        }
     }
 
     private long getDimBehindFadeDuration(long duration) {
@@ -454,11 +456,14 @@
     }
 
     boolean isDimming() {
+        if (mDimLayer == null) {
+            return false;
+        }
         return mDimLayer.isDimming();
     }
 
     boolean isDimming(WindowStateAnimator winAnimator) {
-        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
+        return mDimWinAnimator == winAnimator && isDimming();
     }
 
     void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 806f241..f4e2e75 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8702,7 +8702,8 @@
             displayInfo.overscanBottom = bottom;
         }
 
-        mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, left, top, right, bottom);
+        mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top,
+                right, bottom);
         mDisplaySettings.writeSettingsLocked();
 
         reconfigureDisplayLocked(displayContent);
@@ -10411,8 +10412,8 @@
                     ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
                     " mHasSurface=" + win.mHasSurface +
                     " drawState=" + win.mWinAnimator.mDrawState);
-            if (win.mRemoved || !win.mHasSurface) {
-                // Window has been removed; no draw will now happen, so stop waiting.
+            if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
+                // Window has been removed or hidden; no draw will now happen, so stop waiting.
                 if (DEBUG_SCREEN_ON) Slog.w(TAG, "Aborted waiting for drawn: " + win);
                 mWaitingForDrawn.remove(win);
             } else if (win.hasDrawnLw()) {
@@ -11955,12 +11956,18 @@
                 final WindowList windows = getDefaultWindowListLocked();
                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                     final WindowState win = windows.get(winNdx);
+                    final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
                     if (win.isVisibleLw()
-                            && (win.mAppToken != null || mPolicy.isForceHiding(win.mAttrs))) {
+                            && (win.mAppToken != null || isForceHiding)) {
                         win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
                         // Force add to mResizingWindows.
                         win.mLastContentInsets.set(-1, -1, -1, -1);
                         mWaitingForDrawn.add(win);
+
+                        // No need to wait for the windows below Keyguard.
+                        if (isForceHiding) {
+                            break;
+                        }
                     }
                 }
                 requestTraversalLocked();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ab56d5e..726d29d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1589,8 +1589,6 @@
         final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left;
         final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top;
         if (mSurfaceX != left || mSurfaceY != top) {
-            mSurfaceX = left;
-            mSurfaceY = top;
             if (mAnimating) {
                 // If this window (or its app token) is animating, then the position
                 // of the surface will be re-computed on the next animation frame.
@@ -1598,6 +1596,8 @@
                 // transformation is being applied by the animation.
                 return;
             }
+            mSurfaceX = left;
+            mSurfaceY = top;
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
             SurfaceControl.openTransaction();
             try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cd2885b..dedf1d9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21,6 +21,7 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -44,6 +45,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
+import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -2957,7 +2959,8 @@
             }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
-                utils.requireCredentialEntry(UserHandle.USER_ALL);
+                utils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
+                        UserHandle.USER_ALL);
             }
             synchronized (this) {
                 int newOwner = requireEntry ? callingUid : -1;
@@ -3089,7 +3092,8 @@
             mPowerManager.goToSleep(SystemClock.uptimeMillis(),
                     PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
             // Ensure the device is locked
-            new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
+            new LockPatternUtils(mContext).requireStrongAuth(
+                    STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL);
             getWindowManager().lockNow(null);
         } catch (RemoteException e) {
         } finally {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d28411e..7a8d4ef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -875,6 +875,11 @@
                 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
                     mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
                 }
+
+                if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
+                    Slog.i(TAG, "Gesture Launcher Service");
+                    mSystemServiceManager.startService(GestureLauncherService.class);
+                }
             }
 
             try {
diff --git a/services/net/java/android/net/util/IpUtils.java b/services/net/java/android/net/util/IpUtils.java
new file mode 100644
index 0000000..e037c40
--- /dev/null
+++ b/services/net/java/android/net/util/IpUtils.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+/**
+ * @hide
+ */
+public class IpUtils {
+    /**
+     * Converts a signed short value to an unsigned int value.  Needed
+     * because Java does not have unsigned types.
+     */
+    private static int intAbs(short v) {
+        return v & 0xFFFF;
+    }
+
+    /**
+     * Performs an IP checksum (used in IP header and across UDP
+     * payload) on the specified portion of a ByteBuffer.  The seed
+     * allows the checksum to commence with a specified value.
+     */
+    private static int checksum(ByteBuffer buf, int seed, int start, int end) {
+        int sum = seed;
+        final int bufPosition = buf.position();
+
+        // set position of original ByteBuffer, so that the ShortBuffer
+        // will be correctly initialized
+        buf.position(start);
+        ShortBuffer shortBuf = buf.asShortBuffer();
+
+        // re-set ByteBuffer position
+        buf.position(bufPosition);
+
+        final int numShorts = (end - start) / 2;
+        for (int i = 0; i < numShorts; i++) {
+            sum += intAbs(shortBuf.get(i));
+        }
+        start += numShorts * 2;
+
+        // see if a singleton byte remains
+        if (end != start) {
+            short b = buf.get(start);
+
+            // make it unsigned
+            if (b < 0) {
+                b += 256;
+            }
+
+            sum += b * 256;
+        }
+
+        sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
+        sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
+        int negated = ~sum;
+        return intAbs((short) negated);
+    }
+
+    private static int pseudoChecksumIPv4(
+            ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
+        int partial = protocol + transportLen;
+        partial += intAbs(buf.getShort(headerOffset + 12));
+        partial += intAbs(buf.getShort(headerOffset + 14));
+        partial += intAbs(buf.getShort(headerOffset + 16));
+        partial += intAbs(buf.getShort(headerOffset + 18));
+        return partial;
+    }
+
+    private static int pseudoChecksumIPv6(
+            ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
+        int partial = protocol + transportLen;
+        for (int offset = 8; offset < 40; offset += 2) {
+            partial += intAbs(buf.getShort(headerOffset + offset));
+        }
+        return partial;
+    }
+
+    private static byte ipversion(ByteBuffer buf, int headerOffset) {
+        return (byte) ((buf.get(headerOffset) & (byte) 0xf0) >> 4);
+   }
+
+    public static short ipChecksum(ByteBuffer buf, int headerOffset) {
+        byte ihl = (byte) (buf.get(headerOffset) & 0x0f);
+        return (short) checksum(buf, 0, headerOffset, headerOffset + ihl * 4);
+    }
+
+    private static short transportChecksum(ByteBuffer buf, int protocol,
+            int ipOffset, int transportOffset, int transportLen) {
+        if (transportLen < 0) {
+            throw new IllegalArgumentException("Transport length < 0: " + transportLen);
+        }
+        int sum;
+        byte ver = ipversion(buf, ipOffset);
+        if (ver == 4) {
+            sum = pseudoChecksumIPv4(buf, ipOffset, protocol, transportLen);
+        } else if (ver == 6) {
+            sum = pseudoChecksumIPv6(buf, ipOffset, protocol, transportLen);
+        } else {
+            throw new UnsupportedOperationException("Checksum must be IPv4 or IPv6");
+        }
+
+        sum = checksum(buf, sum, transportOffset, transportOffset + transportLen);
+        if (protocol == IPPROTO_UDP && sum == 0) {
+            sum = (short) 0xffff;
+        }
+        return (short) sum;
+    }
+
+    public static short udpChecksum(ByteBuffer buf, int ipOffset, int transportOffset) {
+        int transportLen = intAbs(buf.getShort(transportOffset + 4));
+        return transportChecksum(buf, IPPROTO_UDP, ipOffset, transportOffset, transportLen);
+    }
+
+    public static short tcpChecksum(ByteBuffer buf, int ipOffset, int transportOffset,
+            int transportLen) {
+        return transportChecksum(buf, IPPROTO_TCP, ipOffset, transportOffset, transportLen);
+    }
+
+    public static String addressAndPortToString(InetAddress address, int port) {
+        return String.format(
+                (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
+                address.getHostAddress(), port);
+    }
+
+    public static boolean isValidUdpOrTcpPort(int port) {
+        return port > 0 && port < 65536;
+    }
+}
diff --git a/services/tests/servicestests/src/android/net/IpUtilsTest.java b/services/tests/servicestests/src/android/net/IpUtilsTest.java
new file mode 100644
index 0000000..c2d1608
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/IpUtilsTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.net.util.IpUtils;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+
+public class IpUtilsTest extends TestCase {
+
+    private static final int IPV4_HEADER_LENGTH = 20;
+    private static final int IPV6_HEADER_LENGTH = 40;
+    private static final int TCP_HEADER_LENGTH = 20;
+    private static final int UDP_HEADER_LENGTH = 8;
+    private static final int IP_CHECKSUM_OFFSET = 10;
+    private static final int TCP_CHECKSUM_OFFSET = 16;
+    private static final int UDP_CHECKSUM_OFFSET = 6;
+
+    private int getUnsignedByte(ByteBuffer buf, int offset) {
+        return buf.get(offset) & 0xff;
+    }
+
+    private int getChecksum(ByteBuffer buf, int offset) {
+        return getUnsignedByte(buf, offset) * 256 + getUnsignedByte(buf, offset + 1);
+    }
+
+    private void assertChecksumEquals(int expected, short actual) {
+        assertEquals(Integer.toHexString(expected), Integer.toHexString(actual & 0xffff));
+    }
+
+    // Generate test packets using Python code like this::
+    //
+    // from scapy import all as scapy
+    //
+    // def JavaPacketDefinition(bytes):
+    //   out = "        ByteBuffer packet = ByteBuffer.wrap(new byte[] {\n            "
+    //   for i in xrange(len(bytes)):
+    //     out += "(byte) 0x%02x" % ord(bytes[i])
+    //     if i < len(bytes) - 1:
+    //       if i % 4 == 3:
+    //         out += ",\n            "
+    //       else:
+    //         out += ", "
+    //   out += "\n        });"
+    //   return out
+    //
+    // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2") /
+    //           scapy.UDP(sport=12345, dport=7) /
+    //           "hello")
+    // print JavaPacketDefinition(str(packet))
+
+    @SmallTest
+    public void testIpv6TcpChecksum() throws Exception {
+        // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80) /
+        //           scapy.TCP(sport=12345, dport=7,
+        //                     seq=1692871236, ack=128376451, flags=16,
+        //                     window=32768) /
+        //           "hello, world")
+        ByteBuffer packet = ByteBuffer.wrap(new byte[] {
+            (byte) 0x68, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x20, (byte) 0x06, (byte) 0x40,
+            (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+            (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+            (byte) 0x30, (byte) 0x39, (byte) 0x00, (byte) 0x07,
+            (byte) 0x64, (byte) 0xe7, (byte) 0x2a, (byte) 0x44,
+            (byte) 0x07, (byte) 0xa6, (byte) 0xde, (byte) 0x83,
+            (byte) 0x50, (byte) 0x10, (byte) 0x80, (byte) 0x00,
+            (byte) 0xee, (byte) 0x71, (byte) 0x00, (byte) 0x00,
+            (byte) 0x68, (byte) 0x65, (byte) 0x6c, (byte) 0x6c,
+            (byte) 0x6f, (byte) 0x2c, (byte) 0x20, (byte) 0x77,
+            (byte) 0x6f, (byte) 0x72, (byte) 0x6c, (byte) 0x64
+        });
+
+        // Check that a valid packet has checksum 0.
+        int transportLen = packet.limit() - IPV6_HEADER_LENGTH;
+        assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+
+        // Check that we can calculate the checksum from scratch.
+        int sumOffset = IPV6_HEADER_LENGTH + TCP_CHECKSUM_OFFSET;
+        int sum = getUnsignedByte(packet, sumOffset) * 256 + getUnsignedByte(packet, sumOffset + 1);
+        assertEquals(0xee71, sum);
+
+        packet.put(sumOffset, (byte) 0);
+        packet.put(sumOffset + 1, (byte) 0);
+        assertChecksumEquals(sum, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+
+        // Check that writing the checksum back into the packet results in a valid packet.
+        packet.putShort(
+            sumOffset,
+            IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+        assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+    }
+
+    @SmallTest
+    public void testIpv4UdpChecksum() {
+        // packet = (scapy.IP(src="192.0.2.1", dst="192.0.2.2", tos=0x40) /
+        //           scapy.UDP(sport=32012, dport=4500) /
+        //           "\xff")
+        ByteBuffer packet = ByteBuffer.wrap(new byte[] {
+            (byte) 0x45, (byte) 0x40, (byte) 0x00, (byte) 0x1d,
+            (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
+            (byte) 0x40, (byte) 0x11, (byte) 0xf6, (byte) 0x8b,
+            (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x01,
+            (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x02,
+            (byte) 0x7d, (byte) 0x0c, (byte) 0x11, (byte) 0x94,
+            (byte) 0x00, (byte) 0x09, (byte) 0xee, (byte) 0x36,
+            (byte) 0xff
+        });
+
+        // Check that a valid packet has IP checksum 0 and UDP checksum 0xffff (0 is not a valid
+        // UDP checksum, so the udpChecksum rewrites 0 to 0xffff).
+        assertEquals(0, IpUtils.ipChecksum(packet, 0));
+        assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+
+        // Check that we can calculate the checksums from scratch.
+        final int ipSumOffset = IP_CHECKSUM_OFFSET;
+        final int ipSum = getChecksum(packet, ipSumOffset);
+        assertEquals(0xf68b, ipSum);
+
+        packet.put(ipSumOffset, (byte) 0);
+        packet.put(ipSumOffset + 1, (byte) 0);
+        assertChecksumEquals(ipSum, IpUtils.ipChecksum(packet, 0));
+
+        final int udpSumOffset = IPV4_HEADER_LENGTH + UDP_CHECKSUM_OFFSET;
+        final int udpSum = getChecksum(packet, udpSumOffset);
+        assertEquals(0xee36, udpSum);
+
+        packet.put(udpSumOffset, (byte) 0);
+        packet.put(udpSumOffset + 1, (byte) 0);
+        assertChecksumEquals(udpSum, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+
+        // Check that writing the checksums back into the packet results in a valid packet.
+        packet.putShort(ipSumOffset, IpUtils.ipChecksum(packet, 0));
+        packet.putShort(udpSumOffset, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+        assertEquals(0, IpUtils.ipChecksum(packet, 0));
+        assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 09e15a8..fb9a3a3 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -19,15 +19,19 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -105,6 +109,7 @@
     private static final int MSG_USER_SWITCHED = 5;
     private static final int MSG_SET_USB_DATA_UNLOCKED = 6;
     private static final int MSG_UPDATE_USER_RESTRICTIONS = 7;
+    private static final int MSG_UPDATE_HOST_STATE = 8;
 
     private static final int AUDIO_MODE_SOURCE = 1;
 
@@ -175,6 +180,15 @@
         }
     };
 
+    private final BroadcastReceiver mHostReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+            UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
+            mHandler.updateHostState(port, status);
+        }
+    };
+
     public UsbDeviceManager(Context context, UsbAlsaManager alsaManager) {
         mContext = context;
         mUsbAlsaManager = alsaManager;
@@ -197,6 +211,8 @@
         if (secureAdbEnabled && !dataEncrypted) {
             mDebuggingManager = new UsbDebuggingManager(context);
         }
+        mContext.registerReceiver(mHostReceiver,
+                new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED));
     }
 
     private UsbSettingsManager getCurrentSettings() {
@@ -299,6 +315,7 @@
 
         // current USB state
         private boolean mConnected;
+        private boolean mHostConnected;
         private boolean mConfigured;
         private boolean mUsbDataUnlocked;
         private String mCurrentFunctions;
@@ -377,6 +394,11 @@
             sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
         }
 
+        public void updateHostState(UsbPort port, UsbPortStatus status) {
+            boolean hostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
+            obtainMessage(MSG_UPDATE_HOST_STATE, hostConnected ? 1 :0, 0).sendToTarget();
+        }
+
         private boolean waitForState(String state) {
             // wait for the transition to complete.
             // give up after 1 second.
@@ -652,6 +674,10 @@
                         updateUsbFunctions();
                     }
                     break;
+                case MSG_UPDATE_HOST_STATE:
+                    mHostConnected = (msg.arg1 == 1);
+                    updateUsbNotification();
+                    break;
                 case MSG_ENABLE_ADB:
                     setAdbEnabled(msg.arg1 == 1);
                     break;
@@ -707,7 +733,7 @@
             if (mNotificationManager == null || !mUseUsbNotification) return;
             int id = 0;
             Resources r = mContext.getResources();
-            if (mConnected) {
+            if (mConnected || mHostConnected) {
                 if (!mUsbDataUnlocked) {
                     id = com.android.internal.R.string.usb_charging_notification_title;
                 } else if (UsbManager.containsFunction(mCurrentFunctions,
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 52abcfe..7f182a4 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -26,8 +26,13 @@
 import android.hardware.usb.UsbPortStatus;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -89,6 +94,9 @@
     private static final String PORT_DATA_ROLE_HOST = "host";
     private static final String PORT_DATA_ROLE_DEVICE = "device";
 
+    private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec.";
+    private static final String USB_TYPEC_STATE = "sys.usb.typec.state";
+
     // All non-trivial role combinations.
     private static final int COMBO_SOURCE_HOST =
             UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
@@ -621,16 +629,25 @@
         return 0;
     }
 
+    private static boolean fileIsRootWritable(String path) {
+        try {
+            // If the file is user writable, then it is root writable.
+            return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0;
+        } catch (ErrnoException e) {
+            return false;
+        }
+    }
+
     private static boolean canChangeMode(File portDir) {
-        return new File(portDir, SYSFS_PORT_MODE).canWrite();
+        return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath());
     }
 
     private static boolean canChangePowerRole(File portDir) {
-        return new File(portDir, SYSFS_PORT_POWER_ROLE).canWrite();
+        return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath());
     }
 
     private static boolean canChangeDataRole(File portDir) {
-        return new File(portDir, SYSFS_PORT_DATA_ROLE).canWrite();
+        return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath());
     }
 
     private static String readFile(File dir, String filename) {
@@ -642,16 +659,29 @@
         }
     }
 
-    private static boolean writeFile(File dir, String filename, String contents) {
-        final File file = new File(dir, filename);
-        try {
-            try (FileWriter writer = new FileWriter(file)) {
-                writer.write(contents);
-            }
-            return true;
-        } catch (IOException ex) {
-            return false;
+    private static boolean waitForState(String property, String state) {
+        // wait for the transition to complete.
+        // give up after 5 seconds.
+        // 5 seconds is probably too long, but we have seen hardware that takes
+        // over 3 seconds to change states.
+        String value = null;
+        for (int i = 0; i < 100; i++) {
+            // State transition is done when property is set to the new configuration
+            value = SystemProperties.get(property);
+            if (state.equals(value)) return true;
+            SystemClock.sleep(50);
         }
+        Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value);
+        return false;
+    }
+
+    private static String propertyFromFilename(String filename) {
+        return USB_TYPEC_PROP_PREFIX + filename;
+    }
+
+    private static boolean writeFile(File dir, String filename, String contents) {
+        SystemProperties.set(propertyFromFilename(filename), contents);
+        return waitForState(USB_TYPEC_STATE, contents);
     }
 
     private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 0a1dbf6..f149f24 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -970,7 +970,6 @@
         unregisterCallback(listener);
     }
 
-
     /** {@hide} */
     Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
         mPhone = phone;
@@ -980,6 +979,14 @@
     }
 
     /** {@hide} */
+    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
+        mPhone = phone;
+        mTelecomCallId = telecomCallId;
+        mInCallAdapter = inCallAdapter;
+        mState = state;
+    }
+
+    /** {@hide} */
     final String internalGetCallId() {
         return mTelecomCallId;
     }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 96f44b9..383e45b 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -656,6 +656,9 @@
                         connection.getDisconnectCause(),
                         createIdList(connection.getConferenceables()),
                         connection.getExtras()));
+        if (isUnknown) {
+            triggerConferenceRecalculate();
+        }
     }
 
     private void abort(String callId) {
@@ -1016,6 +1019,16 @@
     }
 
     /**
+     * Trigger recalculate functinality for conference calls. This is used when a Telephony
+     * Connection is part of a conference controller but is not yet added to Connection
+     * Service and hence cannot be added to the conference call.
+     *
+     * @hide
+     */
+    public void triggerConferenceRecalculate() {
+    }
+
+    /**
      * Create a {@code Connection} given an outgoing request. This is used to initiate new
      * outgoing calls.
      *
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 8eb091b..47154da 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -122,7 +122,8 @@
     }
 
     final void internalAddCall(ParcelableCall parcelableCall) {
-        Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
+        Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
+                parcelableCall.getState());
         mCallByTelecomCallId.put(parcelableCall.getId(), call);
         mCalls.add(call);
         checkCallTree(parcelableCall);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 067e734..8779462 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -367,6 +367,48 @@
             "android.telecom.intent.extra.TTY_PREFERRED";
 
     /**
+     * Broadcast intent action for letting custom component know to show the missed call
+     * notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION =
+            "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
+
+    /**
+     * The number of calls associated with the notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_COUNT =
+            "android.telecom.extra.NOTIFICATION_COUNT";
+
+    /**
+     * The number associated with the missed calls. This number is only relevant
+     * when EXTRA_NOTIFICATION_COUNT is 1.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_PHONE_NUMBER =
+            "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
+
+    /**
+     * The intent to clear missed calls.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT =
+            "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
+
+    /**
+     * The intent to call back a missed call.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_CALL_BACK_INTENT =
+            "android.telecom.extra.CALL_BACK_INTENT";
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e9c41a1..9dc94c8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -259,6 +259,14 @@
             = "carrier_allow_turnoff_ims_bool";
 
     /**
+     * Flag specifying whether IMS instant lettering is available for the carrier.  {@code True} if
+     * instant lettering is available for the carrier, {@code false} otherwise.
+     * @hide
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL =
+            "carrier_instant_lettering_available_bool";
+
+    /**
      * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
      * this is the value that should be used instead. A configuration value of
      * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -334,6 +342,21 @@
             "ci_action_on_sys_update_extra_val_string";
 
     /**
+     * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value
+     * is specified, the UE shall wait for the specified amount of time before it sends out
+     * successive DTMF tones on the network.
+     * @hide
+     */
+    public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+
+    /**
+     * Determines whether conference calls are supported by a carrier.  When {@code true},
+     * conference calling is supported, {@code false otherwise}.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
+
+    /**
      * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
      * prevent manual operator selection. If false, this SIM setting will be ignored and manual
      * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -393,6 +416,7 @@
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
         sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
@@ -430,6 +454,8 @@
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
+        sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
+        sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 79146f3..273cc93 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1135,6 +1135,8 @@
         "VI", // U.S. Virgin Islands
     };
 
+    private static final String KOREA_ISO_COUNTRY_CODE = "KR";
+
     /**
      * Breaks the given number down and formats it according to the rules
      * for the country the number is from.
@@ -1455,7 +1457,14 @@
         String result = null;
         try {
             PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
-            result = util.formatInOriginalFormat(pn, defaultCountryIso);
+            if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
+                    (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE))) {
+                // Format local Korean phone numbers with country code to corresponding national
+                // format which would replace the leading +82 with 0.
+                result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+            } else {
+                result = util.formatInOriginalFormat(pn, defaultCountryIso);
+            }
         } catch (NumberParseException e) {
         }
         return result;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2415165..e104b38 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -602,6 +602,46 @@
     public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
 
     /**
+     * Broadcast intent action for letting custom component know to show voicemail notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION =
+            "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+
+    /**
+     * The number of voice messages associated with the notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_COUNT =
+            "android.telephony.extra.NOTIFICATION_COUNT";
+
+    /**
+     * The voicemail number.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VOICEMAIL_NUMBER =
+            "android.telephony.extra.VOICEMAIL_NUMBER";
+
+    /**
+     * The intent to call voicemail.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_CALL_VOICEMAIL_INTENT =
+            "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+
+    /**
+     * The intent to launch voicemail settings.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT =
+            "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+
+    /**
      * Response codes for sim activation. Activation completed successfully.
      * @hide
      */
@@ -4352,13 +4392,13 @@
        }
    }
 
-   /**
-    * Returns the Status of Volte
-    *@hide
-    */
-   public boolean isVolteEnabled() {
+    /**
+     * Returns the Status of Volte
+     * @hide
+     */
+    public boolean isVolteAvailable() {
        try {
-           return getITelephony().isVolteEnabled();
+           return getITelephony().isVolteAvailable();
        } catch (RemoteException ex) {
            return false;
        } catch (NullPointerException ex) {
@@ -4366,13 +4406,27 @@
        }
    }
 
-   /**
-    * Returns the Status of Wi-Fi Calling
-    *@hide
-    */
-   public boolean isWifiCallingEnabled() {
+    /**
+     * Returns the Status of video telephony (VT)
+     * @hide
+     */
+    public boolean isVideoTelephonyAvailable() {
+        try {
+            return getITelephony().isVideoTelephonyAvailable();
+        } catch (RemoteException ex) {
+            return false;
+        } catch (NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the Status of Wi-Fi Calling
+     * @hide
+     */
+    public boolean isWifiCallingAvailable() {
        try {
-           return getITelephony().isWifiCallingEnabled();
+           return getITelephony().isWifiCallingAvailable();
        } catch (RemoteException ex) {
            return false;
        } catch (NullPointerException ex) {
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 2c4354b..f263b4d 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -174,11 +174,19 @@
      *  cna : Calling name
      *  ussd : For network-initiated USSD, MT only
      *  remote_uri : Connected user identity (it can be used for the conference)
+     *  ChildNum: Child number info.
+     *  Codec: Codec info.
+     *  DisplayText: Display text for the call.
+     *  AdditionalCallInfo: Additional call info.
      */
     public static final String EXTRA_OI = "oi";
     public static final String EXTRA_CNA = "cna";
     public static final String EXTRA_USSD = "ussd";
     public static final String EXTRA_REMOTE_URI = "remote_uri";
+    public static final String EXTRA_CHILD_NUMBER = "ChildNum";
+    public static final String EXTRA_CODEC = "Codec";
+    public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
+    public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
 
     public int mServiceType;
     public int mCallType;
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 9628915..2769a2b 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -99,6 +99,9 @@
     // MT : No action from user after alerting the call
     public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
 
+    //Call failures for FDN
+    public static final int CODE_FDN_BLOCKED = 241;
+
     /**
      * STATUSCODE (SIP response code) (IMS -> Telephony)
      */
@@ -227,6 +230,12 @@
     public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
 
     /**
+     * MT call has ended due to a release from the network
+     * because the call was answered elsewhere
+     */
+    public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+    /**
      * Network string error messages.
      * mExtraMessage may have these values.
      */
diff --git a/telephony/java/com/android/ims/ImsSsInfo.java b/telephony/java/com/android/ims/ImsSsInfo.java
index dbde1c6..7acc3bf 100644
--- a/telephony/java/com/android/ims/ImsSsInfo.java
+++ b/telephony/java/com/android/ims/ImsSsInfo.java
@@ -34,6 +34,7 @@
 
     // 0: disabled, 1: enabled
     public int mStatus;
+    public String mIcbNum;
 
     public ImsSsInfo() {
     }
@@ -50,6 +51,7 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mStatus);
+        out.writeString(mIcbNum);
     }
 
     @Override
@@ -59,6 +61,7 @@
 
     private void readFromParcel(Parcel in) {
         mStatus = in.readInt();
+        mIcbNum = in.readString();
     }
 
     public static final Creator<ImsSsInfo> CREATOR =
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl b/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
new file mode 100644
index 0000000..6b4479f
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.ims;
+
+parcelable ImsSuppServiceNotification;
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.java b/telephony/java/com/android/ims/ImsSuppServiceNotification.java
new file mode 100644
index 0000000..faf7499
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsSuppServiceNotification.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+
+/**
+ * Parcelable object to handle IMS supplementary service notifications.
+ *
+ * @hide
+ */
+public class ImsSuppServiceNotification implements Parcelable {
+    private static final String TAG = "ImsSuppServiceNotification";
+
+    /** Type of notification: 0 = MO; 1 = MT */
+    public int notificationType;
+    /** TS 27.007 7.17 "code1" or "code2" */
+    public int code;
+    /** TS 27.007 7.17 "index" - Not used currently*/
+    public int index;
+    /** TS 27.007 7.17 "type" (MT only) - Not used currently */
+    public int type;
+    /** TS 27.007 7.17 "number" (MT only) */
+    public String number;
+    /** List of forwarded numbers, if any */
+    public String[] history;
+
+    public ImsSuppServiceNotification() {
+    }
+
+    public ImsSuppServiceNotification(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public String toString() {
+        return "{ notificationType=" + notificationType +
+                ", code=" + code +
+                ", index=" + index +
+                ", type=" + type +
+                ", number=" + number +
+                ", history=" + Arrays.toString(history) +
+                " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(notificationType);
+        out.writeInt(code);
+        out.writeInt(index);
+        out.writeInt(type);
+        out.writeString(number);
+        out.writeStringArray(history);
+    }
+
+    private void readFromParcel(Parcel in) {
+        notificationType = in.readInt();
+        code = in.readInt();
+        index = in.readInt();
+        type = in.readInt();
+        number = in.readString();
+        history = in.createStringArray();
+    }
+
+    public static final Creator<ImsSuppServiceNotification> CREATOR =
+            new Creator<ImsSuppServiceNotification>() {
+        @Override
+        public ImsSuppServiceNotification createFromParcel(Parcel in) {
+            return new ImsSuppServiceNotification(in);
+        }
+
+        @Override
+        public ImsSuppServiceNotification[] newArray(int size) {
+            return new ImsSuppServiceNotification[size];
+        }
+    };
+}
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 0443c3e..d562ecc 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -21,6 +21,7 @@
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsConferenceState;
 import com.android.ims.internal.IImsCallSession;
+import com.android.ims.ImsSuppServiceNotification;
 
 /**
  * A listener type for receiving notification on IMS call session events.
@@ -123,4 +124,10 @@
      * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
      */
     void callSessionMultipartyStateChanged(in IImsCallSession session, in boolean isMultiParty);
+
+    /**
+     * Notifies the supplementary service information for the current session.
+     */
+    void callSessionSuppServiceReceived(in IImsCallSession session,
+         in ImsSuppServiceNotification suppSrvNotification);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index c910600..a6a2658 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -72,4 +72,10 @@
      */
     void registrationFeatureCapabilityChanged(int serviceClass,
             out int[] enabledFeatures, out int[] disabledFeatures);
+
+    /**
+     * Updates the application with the waiting voice message count.
+     * @param count The number of waiting voice messages.
+     */
+    void voiceMessageCountUpdate(int count);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index c531ea5..4ab5ee3 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -74,7 +74,7 @@
     /**
      * Updates the configuration of the call barring.
      */
-    int updateCallBarring(int cbType, boolean enable, in String[] barrList);
+    int updateCallBarring(int cbType, int action, in String[] barrList);
 
     /**
      * Updates the configuration of the call forward.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a93e465..661f12d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -951,6 +951,7 @@
      * @return {@code true} if the device supports hearing aid compatibility.
      */
     boolean isHearingAidCompatibilitySupported();
+
     /**
      * Get IMS Registration Status
      */
@@ -958,15 +959,18 @@
 
     /**
      * Returns the Status of Wi-Fi Calling
-     *@hide
      */
-    boolean isWifiCallingEnabled();
+    boolean isWifiCallingAvailable();
+    
+    /**
+     * Returns the Status of Volte
+     */
+    boolean isVolteAvailable();
 
      /**
-     * Returns the Status of Volte
-     *@hide
+     * Returns the Status of VT (video telephony)
      */
-    boolean isVolteEnabled();
+    boolean isVideoTelephonyAvailable();
 
     /**
       * Returns the unique device ID of phone, for example, the IMEI for
@@ -995,7 +999,6 @@
 
     /**
      * Return the modem activity info.
-     *@hide
      */
     ModemActivityInfo getModemActivityInfo();
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index b76ec17..567002e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,6 +33,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
@@ -227,16 +228,18 @@
      * Find the background color for this bar from the theme attributes. Only relevant to StatusBar
      * and NavigationBar.
      * <p/>
-     * Returns 0 if not found.
+     * Returns null if not found.
      *
      * @param colorAttrName the attribute name for the background color
      * @param translucentAttrName the attribute name for the translucency property of the bar.
      *
      * @throws NumberFormatException if color resolved to an invalid string.
      */
-    protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
+    @Nullable
+    protected Integer getBarColor(@NonNull String colorAttrName,
+            @NonNull String translucentAttrName) {
         if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
-            return 0;
+            return null;
         }
         RenderResources renderResources = getContext().getRenderResources();
         // First check if the bar is translucent.
@@ -251,10 +254,11 @@
         if (transparent) {
             return getColor(renderResources, colorAttrName);
         }
-        return 0;
+        return null;
     }
 
-    private static int getColor(RenderResources renderResources, String attr) {
+    @Nullable
+    private static Integer getColor(RenderResources renderResources, String attr) {
         // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
         ResourceValue resource = renderResources.findItemInTheme(attr, true);
         // Form @color/bar to the #AARRGGBB
@@ -275,7 +279,7 @@
                 }
             }
         }
-        return 0;
+        return null;
     }
 
     private ResourceValue getResourceValue(String reference) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 9c89bfe..d50ce23 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -65,8 +65,8 @@
         super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
                 "navigation_bar.xml", simulatedPlatformVersion);
 
-        int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
-        setBackgroundColor(color == 0 ? 0xFF000000 : color);
+        Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+        setBackgroundColor(color == null ? 0xFF000000 : color);
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 2dc7c65..95a5a58 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -71,8 +71,9 @@
         // FIXME: use FILL_H?
         setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
 
-        int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
-        setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
+        Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+        setBackgroundColor(
+                color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index ac7c409..24e1ce7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -421,8 +421,7 @@
                     gc.setComposite(AlphaComposite.Src);
 
                     gc.setColor(new Color(0x00000000, true));
-                    gc.fillRect(0, 0,
-                            mMeasuredScreenWidth, mMeasuredScreenHeight);
+                    gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
 
                     // done
                     gc.dispose();