Merge "Introduced WindowProcessController/Listener (10/n)"
diff --git a/Android.bp b/Android.bp
index 4d2e5e1..dd5ef92 100644
--- a/Android.bp
+++ b/Android.bp
@@ -152,6 +152,9 @@
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
         "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
+        "core/java/android/hardware/face/IFaceService.aidl",
+        "core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl",
+        "core/java/android/hardware/face/IFaceServiceReceiver.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index 43f10fd..049a9d2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3927,6 +3927,7 @@
     method public void readFromParcel(android.os.Parcel);
     method public int sectionCount();
     method public void setAll(boolean);
+    method public void setPrivacyPolicy(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 930ccd0..1e76d4a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -655,6 +655,7 @@
     method public void readFromParcel(android.os.Parcel);
     method public int sectionCount();
     method public void setAll(boolean);
+    method public void setPrivacyPolicy(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8639849..090e584 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -350,8 +350,10 @@
     public static final int OP_START_FOREGROUND = 76;
     /** @hide */
     public static final int OP_BLUETOOTH_SCAN = 77;
+    /** @hide Use the face authentication API. */
+    public static final int OP_USE_FACE = 78;
     /** @hide */
-    public static final int _NUM_OP = 78;
+    public static final int _NUM_OP = 79;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -596,6 +598,11 @@
     /** @hide */
     public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
 
+    /** @hide Use the face authentication API. */
+    public static final String OPSTR_USE_FACE = "android:use_FACE";
+
+
+
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -733,6 +740,7 @@
             OP_MANAGE_IPSEC_TUNNELS,            // MANAGE_IPSEC_HANDOVERS
             OP_START_FOREGROUND,                // START_FOREGROUND
             OP_COARSE_LOCATION,                 // BLUETOOTH_SCAN
+            OP_USE_FACE,                        // FACE
     };
 
     /**
@@ -817,6 +825,7 @@
             OPSTR_MANAGE_IPSEC_TUNNELS,
             OPSTR_START_FOREGROUND,
             OPSTR_BLUETOOTH_SCAN,
+            OPSTR_USE_FACE,
     };
 
     /**
@@ -902,6 +911,7 @@
             "MANAGE_IPSEC_TUNNELS",
             "START_FOREGROUND",
             "BLUETOOTH_SCAN",
+            "USE_FACE",
     };
 
     /**
@@ -987,6 +997,7 @@
             null, // no permission for OP_MANAGE_IPSEC_TUNNELS
             Manifest.permission.FOREGROUND_SERVICE,
             null, // no permission for OP_BLUETOOTH_SCAN
+            Manifest.permission.USE_BIOMETRIC,
     };
 
     /**
@@ -1073,6 +1084,7 @@
             null, // MANAGE_IPSEC_TUNNELS
             null, // START_FOREGROUND
             null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
+            null, // USE_FACE
     };
 
     /**
@@ -1158,6 +1170,7 @@
             false, // MANAGE_IPSEC_HANDOVERS
             false, // START_FOREGROUND
             true, // BLUETOOTH_SCAN
+            false, // USE_FACE
     };
 
     /**
@@ -1242,6 +1255,7 @@
             AppOpsManager.MODE_ERRORED,  // MANAGE_IPSEC_TUNNELS
             AppOpsManager.MODE_ALLOWED,  // OP_START_FOREGROUND
             AppOpsManager.MODE_ALLOWED,  // OP_BLUETOOTH_SCAN
+            AppOpsManager.MODE_ALLOWED,  // USE_FACE
     };
 
     /**
@@ -1330,6 +1344,7 @@
             false, // MANAGE_IPSEC_TUNNELS
             false, // START_FOREGROUND
             false, // BLUETOOTH_SCAN
+            false, // USE_FACE
     };
 
     /**
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ba355f9..1ad3054 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -644,7 +644,7 @@
 
     @Nullable
     private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
-        if (uri == null) {
+        if (uri == null || Uri.EMPTY.equals(uri)) {
             return null;
         }
         ContentResolver contentResolver = context.getContentResolver();
@@ -680,7 +680,7 @@
 
     private Uri getSoundForBackup(Context context) {
         Uri sound = getSound();
-        if (sound == null) {
+        if (sound == null || Uri.EMPTY.equals(sound)) {
             return null;
         }
         Uri canonicalSound = context.getContentResolver().canonicalize(sound);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e5f143c..98dc237 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -54,6 +54,8 @@
 import android.hardware.SystemSensorManager;
 import android.hardware.camera2.CameraManager;
 import android.hardware.display.DisplayManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.hdmi.HdmiControlManager;
@@ -791,6 +793,22 @@
                 return new FingerprintManager(ctx.getOuterContext(), service);
             }});
 
+        registerService(Context.FACE_SERVICE, FaceManager.class,
+                new CachedServiceFetcher<FaceManager>() {
+                    @Override
+                    public FaceManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        final IBinder binder;
+                        if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+                            binder = ServiceManager.getServiceOrThrow(Context.FACE_SERVICE);
+                        } else {
+                            binder = ServiceManager.getService(Context.FACE_SERVICE);
+                        }
+                        IFaceService service = IFaceService.Stub.asInterface(binder);
+                        return new FaceManager(ctx.getOuterContext(), service);
+                    }
+                });
+
         registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
                 new CachedServiceFetcher<TvInputManager>() {
             @Override
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index fde756c..6ad6c25 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -996,17 +996,29 @@
     }
 
     /**
-     * If the current wallpaper is a live wallpaper component, return the
-     * information about that wallpaper.  Otherwise, if it is a static image,
-     * simply return null.
+     * Returns the information about the wallpaper if the current wallpaper is
+     * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+     * this returns null.
      */
     public WallpaperInfo getWallpaperInfo() {
+        return getWallpaperInfo(mContext.getUserId());
+    }
+
+    /**
+     * Returns the information about the wallpaper if the current wallpaper is
+     * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+     * this returns null.
+     *
+     * @param userId Owner of the wallpaper.
+     * @hide
+     */
+    public WallpaperInfo getWallpaperInfo(int userId) {
         try {
             if (sGlobals.mService == null) {
                 Log.w(TAG, "WallpaperService not running");
                 throw new RuntimeException(new DeadSystemException());
             } else {
-                return sGlobals.mService.getWallpaperInfo(mContext.getUserId());
+                return sGlobals.mService.getWallpaperInfo(userId);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7ba4447..5e7f1e4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3237,8 +3237,8 @@
 
     /**
      * Called by a device/profile owner to set the timeout after which unlocking with secondary, non
-     * strong auth (e.g. fingerprint, trust agents) times out, i.e. the user has to use a strong
-     * authentication method like password, pin or pattern.
+     * strong auth (e.g. fingerprint, face, trust agents) times out, i.e. the user has to use a
+     * strong authentication method like password, pin or pattern.
      *
      * <p>This timeout is used internally to reset the timer to require strong auth again after
      * specified timeout each time it has been successfully used.
@@ -3710,7 +3710,6 @@
             | DevicePolicyManager.KEYGUARD_DISABLE_IRIS
             | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
 
-
     /**
      * Disable all current and future keyguard customizations.
      */
@@ -4898,10 +4897,10 @@
     /**
      * @hide
      */
-    public void reportFailedFingerprintAttempt(int userHandle) {
+    public void reportFailedBiometricAttempt(int userHandle) {
         if (mService != null) {
             try {
-                mService.reportFailedFingerprintAttempt(userHandle);
+                mService.reportFailedBiometricAttempt(userHandle);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4911,10 +4910,10 @@
     /**
      * @hide
      */
-    public void reportSuccessfulFingerprintAttempt(int userHandle) {
+    public void reportSuccessfulBiometricAttempt(int userHandle) {
         if (mService != null) {
             try {
-                mService.reportSuccessfulFingerprintAttempt(userHandle);
+                mService.reportSuccessfulBiometricAttempt(userHandle);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e3b160f..c95bc5b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,8 +132,8 @@
     void reportPasswordChanged(int userId);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
-    void reportFailedFingerprintAttempt(int userHandle);
-    void reportSuccessfulFingerprintAttempt(int userHandle);
+    void reportFailedBiometricAttempt(int userHandle);
+    void reportSuccessfulBiometricAttempt(int userHandle);
     void reportKeyguardDismissed(int userHandle);
     void reportKeyguardSecured(int userHandle);
 
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 6d65e3e..9985cc0 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -17,6 +17,7 @@
 package android.app.trust;
 
 import android.app.trust.ITrustListener;
+import android.hardware.biometrics.BiometricSourceType;
 
 /**
  * System private API to comunicate with trust service.
@@ -34,6 +35,6 @@
     boolean isDeviceLocked(int userId);
     boolean isDeviceSecure(int userId);
     boolean isTrustUsuallyManaged(int userId);
-    void unlockedByFingerprintForUser(int userId);
-    void clearAllFingerprints();
+    void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
+    void clearAllBiometricRecognized(in BiometricSourceType target);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 8ab0b70..fb27bed 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -20,6 +20,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.hardware.biometrics.BiometricSourceType;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -195,26 +196,28 @@
     }
 
     /**
-     * Updates the trust state for the user due to the user unlocking via fingerprint.
-     * Should only be called if user authenticated via fingerprint and bouncer can be skipped.
+     * Updates the trust state for the user due to the user unlocking via a biometric sensor.
+     * Should only be called if user authenticated via fingerprint, face, or iris and bouncer
+     * can be skipped.
+     *
      * @param userId
      */
     @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-    public void unlockedByFingerprintForUser(int userId) {
+    public void unlockedByBiometricForUser(int userId, BiometricSourceType source) {
         try {
-            mService.unlockedByFingerprintForUser(userId);
+            mService.unlockedByBiometricForUser(userId, source);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Clears authenticated fingerprints for all users.
+     * Clears authentication by the specified biometric type for all users.
      */
     @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-    public void clearAllFingerprints() {
+    public void clearAllBiometricRecognized(BiometricSourceType source) {
         try {
-            mService.clearAllFingerprints();
+            mService.clearAllBiometricRecognized(source);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c0cfb90..5e96b92 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3005,6 +3005,7 @@
             NSD_SERVICE,
             AUDIO_SERVICE,
             FINGERPRINT_SERVICE,
+            //@hide: FACE_SERVICE,
             MEDIA_ROUTER_SERVICE,
             TELEPHONY_SERVICE,
             TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -3652,6 +3653,18 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.hardware.face.FaceManager} for handling management
+     * of face authentication.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.hardware.face.FaceManager
+     */
+    public static final String FACE_SERVICE = "face";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.media.MediaRouter} for controlling and managing
      * routing of media.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 43b6984..a76bc3a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2245,12 +2245,20 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
-      */
+     */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FACE = "android.hardware.face";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports portrait orientation
      * screens.  For backwards compatibility, you can assume that if neither
      * this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
diff --git a/core/java/android/hardware/biometrics/BiometricSourceType.aidl b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
new file mode 100644
index 0000000..15440d8
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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.hardware.biometrics;
+
+/**
+ * @hide
+ */
+parcelable BiometricSourceType;
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricSourceType.java b/core/java/android/hardware/biometrics/BiometricSourceType.java
new file mode 100644
index 0000000..4a08cf2
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.hardware.biometrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public enum BiometricSourceType implements Parcelable {
+    FINGERPRINT,
+    FACE,
+    IRIS;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(name());
+    }
+
+    public static final Creator<BiometricSourceType> CREATOR = new Creator<BiometricSourceType>() {
+        @Override
+        public BiometricSourceType createFromParcel(final Parcel source) {
+            return BiometricSourceType.valueOf(source.readString());
+        }
+
+        @Override
+        public BiometricSourceType[] newArray(final int size) {
+            return new BiometricSourceType[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a040a09..1ee3c93 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,9 +82,9 @@
  *
  * </ul>
  *
- * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
- * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
- * format may be used.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
+ * {@link ImageFormat#JPEG} and {@link ImageFormat#RAW_PRIVATE} can be used for sharing, subject to
+ * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
  *
  * @see CameraDevice#createCaptureSessionByOutputConfigurations
  *
diff --git a/core/java/android/hardware/face/Face.aidl b/core/java/android/hardware/face/Face.aidl
new file mode 100644
index 0000000..a7c9141
--- /dev/null
+++ b/core/java/android/hardware/face/Face.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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.hardware.face;
+
+/**
+ * @hide
+ */
+parcelable Face;
diff --git a/core/java/android/hardware/face/Face.java b/core/java/android/hardware/face/Face.java
new file mode 100644
index 0000000..c07351d
--- /dev/null
+++ b/core/java/android/hardware/face/Face.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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.hardware.face;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for face metadata.
+ *
+ * @hide
+ */
+public final class Face extends BiometricAuthenticator.BiometricIdentifier {
+    private CharSequence mName;
+    private int mFaceId;
+    private long mDeviceId; // physical device this face is associated with
+
+    public Face(CharSequence name, int faceId, long deviceId) {
+        mName = name;
+        mFaceId = faceId;
+        mDeviceId = deviceId;
+    }
+
+    private Face(Parcel in) {
+        mName = in.readString();
+        mFaceId = in.readInt();
+        mDeviceId = in.readLong();
+    }
+
+    /**
+     * Gets the human-readable name for the given fingerprint.
+     * @return name given to finger
+     */
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the device-specific finger id.  Used by Settings to map a name to a specific
+     * fingerprint template.
+     * @return device-specific id for this finger
+     * @hide
+     */
+    public int getFaceId() {
+        return mFaceId;
+    }
+
+    /**
+     * Device this face belongs to.
+     *
+     * @hide
+     */
+    public long getDeviceId() {
+        return mDeviceId;
+    }
+
+    /**
+     * Describes the contents.
+     * @return
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes to a parcel.
+     * @param out
+     * @param flags Additional flags about how the object should be written.
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mName.toString());
+        out.writeInt(mFaceId);
+        out.writeLong(mDeviceId);
+    }
+
+    public static final Parcelable.Creator<Face> CREATOR = new Parcelable.Creator<Face>() {
+            public Face createFromParcel(Parcel in) {
+                return new Face(in);
+            }
+
+            public Face[] newArray(int size) {
+                return new Face[size];
+            }
+    };
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
new file mode 100644
index 0000000..92f1990
--- /dev/null
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -0,0 +1,1149 @@
+/**
+ * Copyright (C) 2018 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.hardware.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A class that coordinates access to the face authentication hardware.
+ * @hide
+ */
+@SystemService(Context.FACE_SERVICE)
+public class FaceManager {
+    /**
+     * The hardware is unavailable. Try again later.
+     */
+    public static final int FACE_ERROR_HW_UNAVAILABLE = 1;
+    /**
+     * Error state returned when the sensor was unable to process the current image.
+     */
+    public static final int FACE_ERROR_UNABLE_TO_PROCESS = 2;
+    /**
+     * Error state returned when the current request has been running too long. This is intended to
+     * prevent programs from waiting for the face authentication sensor indefinitely. The timeout is
+     * platform and sensor-specific, but is generally on the order of 30 seconds.
+     */
+    public static final int FACE_ERROR_TIMEOUT = 3;
+    /**
+     * Error state returned for operations like enrollment; the operation cannot be completed
+     * because there's not enough storage remaining to complete the operation.
+     */
+    public static final int FACE_ERROR_NO_SPACE = 4;
+    /**
+     * The operation was canceled because the face authentication sensor is unavailable. For
+     * example, this may happen when the user is switched, the device is locked or another pending
+     * operation prevents or disables it.
+     */
+    public static final int FACE_ERROR_CANCELED = 5;
+    /**
+     * The {@link FaceManager#remove} call failed. Typically this will happen when the
+     * provided face id was incorrect.
+     *
+     * @hide
+     */
+    public static final int FACE_ERROR_UNABLE_TO_REMOVE = 6;
+    /**
+     * The operation was canceled because the API is locked out due to too many attempts.
+     * This occurs after 5 failed attempts, and lasts for 30 seconds.
+     */
+    public static final int FACE_ERROR_LOCKOUT = 7;
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * These messages are typically reserved for internal operations such as enrollment, but may be
+     * used to express vendor errors not covered by the ones in HAL h file. Applications are
+     * expected to show the error message string if they happen, but are advised not to rely on the
+     * message id since they will be device and vendor-specific
+     */
+    public static final int FACE_ERROR_VENDOR = 8;
+    //
+    // Error messages from face authentication hardware during initialization, enrollment,
+    // authentication or removal. Must agree with the list in HAL h file
+    //
+    /**
+     * The operation was canceled because FACE_ERROR_LOCKOUT occurred too many times.
+     * Face authentication is disabled until the user unlocks with strong authentication
+     * (PIN/Pattern/Password)
+     */
+    public static final int FACE_ERROR_LOCKOUT_PERMANENT = 9;
+    /**
+     * The user canceled the operation. Upon receiving this, applications should use alternate
+     * authentication (e.g. a password). The application should also provide the means to return
+     * to face authentication, such as a "use face authentication" button.
+     */
+    public static final int FACE_ERROR_USER_CANCELED = 10;
+    /**
+     * The user does not have a face enrolled.
+     */
+    public static final int FACE_ERROR_NOT_ENROLLED = 11;
+    /**
+     * The device does not have a face sensor. This message will propagate if the calling app
+     * ignores the result from PackageManager.hasFeature(FEATURE_FACE) and calls
+     * this API anyway. Apps should always check for the feature before calling this API.
+     */
+    public static final int FACE_ERROR_HW_NOT_PRESENT = 12;
+    /**
+     * @hide
+     */
+    public static final int FACE_ERROR_VENDOR_BASE = 1000;
+    /**
+     * The image acquired was good.
+     */
+    public static final int FACE_ACQUIRED_GOOD = 0;
+    /**
+     * The face image was not good enough to process due to a detected condition.
+     * (See {@link #FACE_ACQUIRED_TOO_BRIGHT or @link #FACE_ACQUIRED_TOO_DARK}).
+     */
+    public static final int FACE_ACQUIRED_INSUFFICIENT = 1;
+    /**
+     * The face image was too bright due to too much ambient light.
+     * For example, it's reasonable to return this after multiple
+     * {@link #FACE_ACQUIRED_INSUFFICIENT}
+     * The user is expected to take action to retry in better lighting conditions
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_BRIGHT = 2;
+    /**
+     * The face image was too dark due to illumination light obscured.
+     * For example, it's reasonable to return this after multiple
+     * {@link #FACE_ACQUIRED_INSUFFICIENT}
+     * The user is expected to take action to retry in better lighting conditions
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_DARK = 3;
+    /**
+     * The detected face is too close to the sensor, and the image can't be processed.
+     * The user should be informed to move farther from the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_CLOSE = 4;
+    /**
+     * The detected face is too small, as the user might be too far from the sensor.
+     * The user should be informed to move closer to the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_FAR = 5;
+    /**
+     * Only the upper part of the face was detected. The sensor field of view is too high.
+     * The user should be informed to move up with respect to the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_HIGH = 6;
+    /**
+     * Only the lower part of the face was detected. The sensor field of view is too low.
+     * The user should be informed to move down with respect to the sensor when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_LOW = 7;
+
+    //
+    // Image acquisition messages. Must agree with those in HAL h file
+    //
+    /**
+     * Only the right part of the face was detected. The sensor field of view is too far right.
+     * The user should be informed to move to the right with respect to the sensor
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_RIGHT = 8;
+    /**
+     * Only the left part of the face was detected. The sensor field of view is too far left.
+     * The user should be informed to move to the left with respect to the sensor
+     * when this is returned.
+     */
+    public static final int FACE_ACQUIRED_TOO_LEFT = 9;
+    /**
+     * User's gaze strayed too far from the sensor causing significant parts of the user's face
+     * to be hidden.
+     * The user should be informed to turn the face front to the sensor.
+     */
+    public static final int FACE_ACQUIRED_POOR_GAZE = 10;
+    /**
+     * No face was detected in front of the sensor.
+     * The user should be informed to point the sensor to a face when this is returned.
+     */
+    public static final int FACE_ACQUIRED_NOT_DETECTED = 11;
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     *
+     * @hide
+     */
+    public static final int FACE_ACQUIRED_VENDOR = 12;
+    /**
+     * @hide
+     */
+    public static final int FACE_ACQUIRED_VENDOR_BASE = 1000;
+    private static final String TAG = "FaceManager";
+    private static final boolean DEBUG = true;
+    private static final int MSG_ENROLL_RESULT = 100;
+    private static final int MSG_ACQUIRED = 101;
+    private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
+    private static final int MSG_AUTHENTICATION_FAILED = 103;
+    private static final int MSG_ERROR = 104;
+    private static final int MSG_REMOVED = 105;
+    private final Context mContext;
+    private IFaceService mService;
+    private IBinder mToken = new Binder();
+    private AuthenticationCallback mAuthenticationCallback;
+    private EnrollmentCallback mEnrollmentCallback;
+    private RemovalCallback mRemovalCallback;
+    private CryptoObject mCryptoObject;
+    private Face mRemovalFace;
+    private Handler mHandler;
+    private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
+
+        @Override // binder call
+        public void onEnrollResult(long deviceId, int faceId, int remaining) {
+            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
+                    new Face(null, faceId, deviceId)).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
+            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onAuthenticationSucceeded(long deviceId, Face face) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, face).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onAuthenticationFailed(long deviceId) {
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onError(long deviceId, int error, int vendorCode) {
+            mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
+        }
+
+        @Override // binder call
+        public void onRemoved(long deviceId, int faceId, int remaining) {
+            mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
+                    new Face(null, faceId, deviceId)).sendToTarget();
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public FaceManager(Context context, IFaceService service) {
+        mContext = context;
+        mService = service;
+        if (mService == null) {
+            Slog.v(TAG, "FaceAuthenticationManagerService was null");
+        }
+        mHandler = new MyHandler(context);
+    }
+
+    /**
+     * Request authentication of a crypto object. This call operates the face recognition hardware
+     * and starts capturing images. It terminates when
+     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param crypto   object associated with the call or null if none required.
+     * @param cancel   an object that can be used to cancel authentication
+     * @param flags    optional flags; should be 0
+     * @param callback an object to receive authentication events
+     * @param handler  an optional handler to handle callback events
+     * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
+     *                                  by
+     *                                  <a href="{@docRoot}training/articles/keystore.html">Android
+     *                                  Keystore facility</a>.
+     * @throws IllegalStateException    if the crypto primitive is not initialized.
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+        authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
+    }
+
+    /**
+     * Use the provided handler thread for events.
+     */
+    private void useHandler(Handler handler) {
+        if (handler != null) {
+            mHandler = new MyHandler(handler.getLooper());
+        } else if (mHandler.getLooper() != mContext.getMainLooper()) {
+            mHandler = new MyHandler(mContext.getMainLooper());
+        }
+    }
+
+    /**
+     * Per-user version
+     *
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an authentication callback");
+        }
+
+        if (cancel != null) {
+            if (cancel.isCanceled()) {
+                Log.w(TAG, "authentication already canceled");
+                return;
+            } else {
+                cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+            }
+        }
+
+        if (mService != null) {
+            try {
+                useHandler(handler);
+                mAuthenticationCallback = callback;
+                mCryptoObject = crypto;
+                long sessionId = crypto != null ? crypto.getOpId() : 0;
+                mService.authenticate(mToken, sessionId, mServiceReceiver, flags,
+                        mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception while authenticating: ", e);
+                if (callback != null) {
+                    // Though this may not be a hardware issue, it will cause apps to give up or try
+                    // again later.
+                    callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                }
+            }
+        }
+    }
+
+    /**
+     * Request face authentication enrollment. This call operates the face authentication hardware
+     * and starts capturing images. Progress will be indicated by callbacks to the
+     * {@link EnrollmentCallback} object. It terminates when
+     * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+     * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param token    a unique token provided by a recent creation or verification of device
+     *                 credentials (e.g. pin, pattern or password).
+     * @param cancel   an object that can be used to cancel enrollment
+     * @param flags    optional flags
+     * @param userId   the user to whom this face will belong to
+     * @param callback an object to receive enrollment events
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public void enroll(byte[] token, CancellationSignal cancel, int flags,
+            int userId, EnrollmentCallback callback) {
+        if (userId == UserHandle.USER_CURRENT) {
+            userId = getCurrentUserId();
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an enrollment callback");
+        }
+
+        if (cancel != null) {
+            if (cancel.isCanceled()) {
+                Log.w(TAG, "enrollment already canceled");
+                return;
+            } else {
+                cancel.setOnCancelListener(new OnEnrollCancelListener());
+            }
+        }
+
+        if (mService != null) {
+            try {
+                mEnrollmentCallback = callback;
+                mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+                        mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception in enroll: ", e);
+                if (callback != null) {
+                    // Though this may not be a hardware issue, it will cause apps to give up or try
+                    // again later.
+                    callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                }
+            }
+        }
+    }
+
+    /**
+     * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
+     * existing device credentials (e.g. pin/pattern/password).
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public long preEnroll() {
+        long result = 0;
+        if (mService != null) {
+            try {
+                result = mService.preEnroll(mToken);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Finishes enrollment and cancels the current auth token.
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public int postEnroll() {
+        int result = 0;
+        if (mService != null) {
+            try {
+                result = mService.postEnroll(mToken);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Sets the active user. This is meant to be used to select the current profile for enrollment
+     * to allow separate enrolled faces for a work profile
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public void setActiveUser(int userId) {
+        if (mService != null) {
+            try {
+                mService.setActiveUser(userId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Remove given face template from face hardware and/or protected storage.
+     *
+     * @param face     the face item to remove
+     * @param userId   the user who this face belongs to
+     * @param callback an optional callback to verify that face templates have been
+     *                 successfully removed. May be null if no callback is required.
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FACE)
+    public void remove(Face face, int userId, RemovalCallback callback) {
+        if (mService != null) {
+            try {
+                mRemovalCallback = callback;
+                mRemovalFace = face;
+                mService.remove(mToken, userId, mServiceReceiver);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception in remove: ", e);
+                if (callback != null) {
+                    callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
+                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                }
+            }
+        }
+    }
+
+    /**
+     * Obtain the enrolled face template.
+     *
+     * @return the current face item
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public Face getEnrolledFace(int userId) {
+        if (mService != null) {
+            try {
+                return mService.getEnrolledFace(userId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Obtain the enrolled face template.
+     *
+     * @return the current face item
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public Face getEnrolledFace() {
+        return getEnrolledFace(UserHandle.myUserId());
+    }
+
+    /**
+     * Determine if there is a face enrolled.
+     *
+     * @return true if a face is enrolled, false otherwise
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public boolean hasEnrolledFace() {
+        if (mService != null) {
+            try {
+                return mService.hasEnrolledFace(
+                        UserHandle.myUserId(), mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            USE_BIOMETRIC,
+            INTERACT_ACROSS_USERS})
+    public boolean hasEnrolledFace(int userId) {
+        if (mService != null) {
+            try {
+                return mService.hasEnrolledFace(userId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determine if face authentication sensor hardware is present and functional.
+     *
+     * @return true if hardware is present and functional, false otherwise.
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public boolean isHardwareDetected() {
+        if (mService != null) {
+            try {
+                long deviceId = 0; /* TODO: plumb hardware id to FPMS */
+                return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
+        }
+        return false;
+    }
+
+    /**
+     * Retrieves the authenticator token for binding keys to the lifecycle
+     * of the calling user's face. Used only by internal clients.
+     *
+     * @hide
+     */
+    public long getAuthenticatorId() {
+        if (mService != null) {
+            try {
+                return mService.getAuthenticatorId(mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+        }
+        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) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "resetTimeout(): Service not connected!");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void addLockoutResetCallback(final LockoutResetCallback callback) {
+        if (mService != null) {
+            try {
+                final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+                mService.addLockoutResetCallback(
+                        new IFaceServiceLockoutResetCallback.Stub() {
+
+                            @Override
+                            public void onLockoutReset(long deviceId,
+                                    IRemoteCallback serverCallback)
+                                    throws RemoteException {
+                                try {
+                                    final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
+                                            PowerManager.PARTIAL_WAKE_LOCK,
+                                            "faceLockoutResetCallback");
+                                    wakeLock.acquire();
+                                    mHandler.post(() -> {
+                                        try {
+                                            callback.onLockoutReset();
+                                        } finally {
+                                            wakeLock.release();
+                                        }
+                                    });
+                                } finally {
+                                    serverCallback.sendResult(null /* data */);
+                                }
+                            }
+                        });
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
+        }
+    }
+
+    private int getCurrentUserId() {
+        try {
+            return ActivityManager.getService().getCurrentUser().id;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void cancelEnrollment() {
+        if (mService != null) {
+            try {
+                mService.cancelEnrollment(mToken);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private void cancelAuthentication(CryptoObject cryptoObject) {
+        if (mService != null) {
+            try {
+                mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private String getErrorString(int errMsg, int vendorCode) {
+        switch (errMsg) {
+            case FACE_ERROR_HW_UNAVAILABLE:
+                return mContext.getString(
+                        com.android.internal.R.string.face_error_hw_not_available);
+            case FACE_ERROR_UNABLE_TO_PROCESS:
+                return mContext.getString(
+                        com.android.internal.R.string.face_error_unable_to_process);
+            case FACE_ERROR_TIMEOUT:
+                return mContext.getString(com.android.internal.R.string.face_error_timeout);
+            case FACE_ERROR_NO_SPACE:
+                return mContext.getString(com.android.internal.R.string.face_error_no_space);
+            case FACE_ERROR_CANCELED:
+                return mContext.getString(com.android.internal.R.string.face_error_canceled);
+            case FACE_ERROR_LOCKOUT:
+                return mContext.getString(com.android.internal.R.string.face_error_lockout);
+            case FACE_ERROR_LOCKOUT_PERMANENT:
+                return mContext.getString(
+                        com.android.internal.R.string.face_error_lockout_permanent);
+            case FACE_ERROR_NOT_ENROLLED:
+                return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+            case FACE_ERROR_HW_NOT_PRESENT:
+                return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+            case FACE_ERROR_VENDOR: {
+                String[] msgArray = mContext.getResources().getStringArray(
+                        com.android.internal.R.array.face_error_vendor);
+                if (vendorCode < msgArray.length) {
+                    return msgArray[vendorCode];
+                }
+            }
+        }
+        Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
+        return null;
+    }
+
+    private String getAcquiredString(int acquireInfo, int vendorCode) {
+        switch (acquireInfo) {
+            case FACE_ACQUIRED_GOOD:
+                return null;
+            case FACE_ACQUIRED_INSUFFICIENT:
+                return mContext.getString(R.string.face_acquired_insufficient);
+            case FACE_ACQUIRED_TOO_BRIGHT:
+                return mContext.getString(R.string.face_acquired_too_bright);
+            case FACE_ACQUIRED_TOO_DARK:
+                return mContext.getString(R.string.face_acquired_too_dark);
+            case FACE_ACQUIRED_TOO_CLOSE:
+                return mContext.getString(R.string.face_acquired_too_close);
+            case FACE_ACQUIRED_TOO_FAR:
+                return mContext.getString(R.string.face_acquired_too_far);
+            case FACE_ACQUIRED_TOO_HIGH:
+                return mContext.getString(R.string.face_acquired_too_high);
+            case FACE_ACQUIRED_TOO_LOW:
+                return mContext.getString(R.string.face_acquired_too_low);
+            case FACE_ACQUIRED_TOO_RIGHT:
+                return mContext.getString(R.string.face_acquired_too_right);
+            case FACE_ACQUIRED_TOO_LEFT:
+                return mContext.getString(R.string.face_acquired_too_left);
+            case FACE_ACQUIRED_POOR_GAZE:
+                return mContext.getString(R.string.face_acquired_poor_gaze);
+            case FACE_ACQUIRED_NOT_DETECTED:
+                return mContext.getString(R.string.face_acquired_not_detected);
+            case FACE_ACQUIRED_VENDOR: {
+                String[] msgArray = mContext.getResources().getStringArray(
+                        R.array.face_acquired_vendor);
+                if (vendorCode < msgArray.length) {
+                    return msgArray[vendorCode];
+                }
+            }
+        }
+        Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
+        return null;
+    }
+
+    /**
+     * A wrapper class for the crypto objects supported by FaceAuthenticationManager.
+     */
+    public static final class CryptoObject {
+
+        private final Object mCrypto;
+
+        public CryptoObject(@NonNull Signature signature) {
+            mCrypto = signature;
+        }
+
+        public CryptoObject(@NonNull Cipher cipher) {
+            mCrypto = cipher;
+        }
+
+        public CryptoObject(@NonNull Mac mac) {
+            mCrypto = mac;
+        }
+
+        /**
+         * Get {@link Signature} object.
+         *
+         * @return {@link Signature} object or null if this doesn't contain one.
+         */
+        public Signature getSignature() {
+            return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+        }
+
+        /**
+         * Get {@link Cipher} object.
+         *
+         * @return {@link Cipher} object or null if this doesn't contain one.
+         */
+        public Cipher getCipher() {
+            return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+        }
+
+        /**
+         * Get {@link Mac} object.
+         *
+         * @return {@link Mac} object or null if this doesn't contain one.
+         */
+        public Mac getMac() {
+            return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+        }
+
+        /**
+         * @return the opId associated with this object or 0 if none
+         * @hide
+         */
+        public long getOpId() {
+            return mCrypto != null
+                    ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+        }
+    }
+
+    /**
+     * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
+     * CancellationSignal, int, AuthenticationCallback, Handler)}.
+     */
+    public static class AuthenticationResult {
+        private Face mFace;
+        private CryptoObject mCryptoObject;
+        private int mUserId;
+
+        /**
+         * Authentication result
+         *
+         * @param crypto the crypto object
+         * @param face   the recognized face data, if allowed.
+         * @hide
+         */
+        public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
+            mCryptoObject = crypto;
+            mFace = face;
+            mUserId = userId;
+        }
+
+        /**
+         * Obtain the crypto object associated with this transaction
+         *
+         * @return crypto object provided to {@link FaceManager#authenticate
+         * (CryptoObject,
+         * CancellationSignal, int, AuthenticationCallback, Handler)}.
+         */
+        public CryptoObject getCryptoObject() {
+            return mCryptoObject;
+        }
+
+        /**
+         * Obtain the Face associated with this operation. Applications are strongly
+         * discouraged from associating specific faces with specific applications or operations.
+         *
+         * @hide
+         */
+        public Face getFace() {
+            return mFace;
+        }
+
+        /**
+         * Obtain the userId for which this face was authenticated.
+         *
+         * @hide
+         */
+        public int getUserId() {
+            return mUserId;
+        }
+    }
+
+    /**
+     * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
+     * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
+     * FaceManager#authenticate(CryptoObject, CancellationSignal,
+     * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
+     * to face events.
+     */
+    public abstract static class AuthenticationCallback {
+
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further callbacks will be made on this object.
+         *
+         * @param errorCode An integer identifying the error message
+         * @param errString A human-readable error string that can be shown in UI
+         */
+        public void onAuthenticationError(int errorCode, CharSequence errString) {
+        }
+
+        /**
+         * Called when a recoverable error has been encountered during authentication. The help
+         * string is provided to give the user guidance for what went wrong, such as
+         * "Sensor dirty, please clean it."
+         *
+         * @param helpCode   An integer identifying the error message
+         * @param helpString A human-readable string that can be shown in UI
+         */
+        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
+        }
+
+        /**
+         * Called when a face is recognized.
+         *
+         * @param result An object containing authentication-related data
+         */
+        public void onAuthenticationSucceeded(AuthenticationResult result) {
+        }
+
+        /**
+         * Called when a face is detected but not recognized.
+         */
+        public void onAuthenticationFailed() {
+        }
+
+        /**
+         * Called when a face image has been acquired, but wasn't processed yet.
+         *
+         * @param acquireInfo one of FACE_ACQUIRED_* constants
+         * @hide
+         */
+        public void onAuthenticationAcquired(int acquireInfo) {
+        }
+    }
+
+    /**
+     * Callback structure provided to {@link FaceManager#enroll(long,
+     * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
+     * must provide an implementation of this to {@link FaceManager#enroll(long,
+     * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
+     *
+     * @hide
+     */
+    public abstract static class EnrollmentCallback {
+
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further callbacks will be made on this object.
+         *
+         * @param errMsgId  An integer identifying the error message
+         * @param errString A human-readable error string that can be shown in UI
+         */
+        public void onEnrollmentError(int errMsgId, CharSequence errString) {
+        }
+
+        /**
+         * Called when a recoverable error has been encountered during enrollment. The help
+         * string is provided to give the user guidance for what went wrong, such as
+         * "Image too dark, uncover light source" or what they need to do next, such as
+         * "Rotate face up / down."
+         *
+         * @param helpMsgId  An integer identifying the error message
+         * @param helpString A human-readable string that can be shown in UI
+         */
+        public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+        }
+
+        /**
+         * Called as each enrollment step progresses. Enrollment is considered complete when
+         * remaining reaches 0. This function will not be called if enrollment fails. See
+         * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
+         *
+         * @param remaining The number of remaining steps
+         * @param vendorMsg Vendor feedback about the current enroll attempt. Use it to customize
+         *                  the GUI according to vendor's requirements.
+         */
+        public void onEnrollmentProgress(int remaining, long vendorMsg) {
+        }
+    }
+
+    /**
+     * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
+     * may
+     * optionally provide an implementation of this to
+     * {@link #remove(Face, int, RemovalCallback)} for listening to face template
+     * removal events.
+     *
+     * @hide
+     */
+    public abstract static class RemovalCallback {
+
+        /**
+         * Called when the given face can't be removed.
+         *
+         * @param face      The face that the call attempted to remove
+         * @param errMsgId  An associated error message id
+         * @param errString An error message indicating why the face id can't be removed
+         */
+        public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
+        }
+
+        /**
+         * Called when a given face is successfully removed.
+         *
+         * @param face The face template that was removed.
+         */
+        public void onRemovalSucceeded(Face face) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public abstract static class LockoutResetCallback {
+
+        /**
+         * Called when lockout period expired and clients are allowed to listen for face
+         * authentication
+         * again.
+         */
+        public void onLockoutReset() {
+        }
+    }
+
+    private class OnEnrollCancelListener implements OnCancelListener {
+        @Override
+        public void onCancel() {
+            cancelEnrollment();
+        }
+    }
+
+    private class OnAuthenticationCancelListener implements OnCancelListener {
+        private CryptoObject mCrypto;
+
+        OnAuthenticationCancelListener(CryptoObject crypto) {
+            mCrypto = crypto;
+        }
+
+        @Override
+        public void onCancel() {
+            cancelAuthentication(mCrypto);
+        }
+    }
+
+    private class MyHandler extends Handler {
+        private MyHandler(Context context) {
+            super(context.getMainLooper());
+        }
+
+        private MyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(android.os.Message msg) {
+            switch (msg.what) {
+                case MSG_ENROLL_RESULT:
+                    sendEnrollResult((EnrollResultMsg) msg.obj);
+                    break;
+                case MSG_ACQUIRED:
+                    sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
+                            msg.arg2 /* vendorCode */);
+                    break;
+                case MSG_AUTHENTICATION_SUCCEEDED:
+                    sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
+                    break;
+                case MSG_AUTHENTICATION_FAILED:
+                    sendAuthenticatedFailed();
+                    break;
+                case MSG_ERROR:
+                    sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
+                            msg.arg2 /* vendorCode */);
+                    break;
+                case MSG_REMOVED:
+                    sendRemovedResult((Face) msg.obj);
+                    break;
+            }
+        }
+
+        private void sendRemovedResult(Face face) {
+            if (mRemovalCallback == null) {
+                return;
+            }
+            if (face == null) {
+                Log.e(TAG, "Received MSG_REMOVED, but face is null");
+                return;
+            }
+
+
+            mRemovalCallback.onRemovalSucceeded(face);
+        }
+
+        private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
+            // emulate HAL 2.1 behavior and send real errMsgId
+            final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
+                    ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
+            if (mEnrollmentCallback != null) {
+                mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
+                        getErrorString(errMsgId, vendorCode));
+            } else if (mAuthenticationCallback != null) {
+                mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
+                        getErrorString(errMsgId, vendorCode));
+            } else if (mRemovalCallback != null) {
+                mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
+                        getErrorString(errMsgId, vendorCode));
+            }
+        }
+
+        private void sendEnrollResult(EnrollResultMsg faceWrapper) {
+            if (mEnrollmentCallback != null) {
+                int remaining = faceWrapper.getRemaining();
+                long vendorMsg = faceWrapper.getVendorMsg();
+                mEnrollmentCallback.onEnrollmentProgress(remaining, vendorMsg);
+            }
+        }
+
+        private void sendAuthenticatedSucceeded(Face face, int userId) {
+            if (mAuthenticationCallback != null) {
+                final AuthenticationResult result =
+                        new AuthenticationResult(mCryptoObject, face, userId);
+                mAuthenticationCallback.onAuthenticationSucceeded(result);
+            }
+        }
+
+        private void sendAuthenticatedFailed() {
+            if (mAuthenticationCallback != null) {
+                mAuthenticationCallback.onAuthenticationFailed();
+            }
+        }
+
+        private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
+            if (mAuthenticationCallback != null) {
+                mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+            }
+            final String msg = getAcquiredString(acquireInfo, vendorCode);
+            if (msg == null) {
+                return;
+            }
+            final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
+                    ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
+            if (mEnrollmentCallback != null) {
+                mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
+            } else if (mAuthenticationCallback != null) {
+                mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+            }
+        }
+    }
+
+    private class EnrollResultMsg {
+        private final Face mFace;
+        private final int mRemaining;
+        private final long mVendorMsg;
+
+        EnrollResultMsg(Face face, int remaining, long vendorMsg) {
+            mFace = face;
+            mRemaining = remaining;
+            mVendorMsg = vendorMsg;
+        }
+
+        Face getFace() {
+            return mFace;
+        }
+
+        long getVendorMsg() {
+            return mVendorMsg;
+        }
+
+        int getRemaining() {
+            return mRemaining;
+        }
+    }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
new file mode 100644
index 0000000..856a313
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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.hardware.face;
+
+import android.os.Bundle;
+import android.hardware.face.IFaceServiceReceiver;
+import android.hardware.face.IFaceServiceLockoutResetCallback;
+import android.hardware.face.Face;
+
+/**
+ * Communication channel from client to the face service.
+ * @hide
+ */
+interface IFaceService {
+    // Authenticate the given sessionId with a face
+    void authenticate(IBinder token, long sessionId,
+            IFaceServiceReceiver receiver, int flags, String opPackageName);
+
+    // Cancel authentication for the given sessionId
+    void cancelAuthentication(IBinder token, String opPackageName);
+
+    // Start face enrollment
+    void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
+                int flags, String opPackageName);
+
+    // Cancel enrollment in progress
+    void cancelEnrollment(IBinder token);
+
+    // Any errors resulting from this call will be returned to the listener
+    void remove(IBinder token, int userId, IFaceServiceReceiver receiver);
+
+    // Get the enrolled face for user.
+    Face getEnrolledFace(int userId, String opPackageName);
+
+    // Determine if HAL is loaded and ready
+    boolean isHardwareDetected(long deviceId, String opPackageName);
+
+    // Get a pre-enrollment authentication token
+    long preEnroll(IBinder token);
+
+    // Finish an enrollment sequence and invalidate the authentication token
+    int postEnroll(IBinder token);
+
+    // Determine if a user has enrolled a face
+    boolean hasEnrolledFace(int userId, String opPackageName);
+
+    // Gets the number of hardware devices
+    // int getHardwareDeviceCount();
+
+    // Gets the unique device id for hardware enumerated at i
+    // long getHardwareDevice(int i);
+
+    // Gets the authenticator ID for face
+    long getAuthenticatorId(String opPackageName);
+
+    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetTimeout(in byte [] cryptoToken);
+
+    // Add a callback which gets notified when the face lockout period expired.
+    void addLockoutResetCallback(IFaceServiceLockoutResetCallback callback);
+
+    // Explicitly set the active user (for enrolling work profile)
+    void setActiveUser(int uid);
+}
diff --git a/core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl b/core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl
new file mode 100644
index 0000000..b62fde3
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.hardware.face;
+
+import android.hardware.face.Face;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+import android.os.UserHandle;
+
+/**
+ * Callback when lockout period expired and clients are allowed to authenticate again.
+ * @hide
+ */
+oneway interface IFaceServiceLockoutResetCallback {
+
+    /**
+     * A wakelock will be held until the reciever calls back into {@param callback}
+     */
+    void onLockoutReset(long deviceId, IRemoteCallback callback);
+}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
new file mode 100644
index 0000000..16fb690
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.hardware.face;
+
+import android.hardware.face.Face;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FaceService back to FaceAuthenticationManager.
+ * @hide
+ */
+oneway interface IFaceServiceReceiver {
+    void onEnrollResult(long deviceId, int faceId, int remaining);
+    void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
+    void onAuthenticationSucceeded(long deviceId, in Face face);
+    void onAuthenticationFailed(long deviceId);
+    void onError(long deviceId, int error, int vendorCode);
+    void onRemoved(long deviceId, int faceId, int remaining);
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b4c8a5e..431c651 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -370,19 +370,6 @@
     InputConnection mStartedInputConnection;
     EditorInfo mInputEditorInfo;
 
-    /**
-     * A token to keep tracking the last IPC that triggered
-     * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
-     * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
-     * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
-     *
-     * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
-     * disentangle event flows for various purposes such as better window animation and providing
-     * fine-grained debugging information.</p>
-     */
-    @Nullable
-    private IBinder mStartInputToken;
-
     int mShowInputFlags;
     boolean mShowInputRequested;
     boolean mLastShowInputRequested;
@@ -528,7 +515,7 @@
         public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
                 @NonNull EditorInfo editorInfo, boolean restarting,
                 @NonNull IBinder startInputToken) {
-            mStartInputToken = startInputToken;
+            mImm.reportStartInput(mToken, startInputToken);
 
             // This needs to be dispatched to interface methods rather than doStartInput().
             // Otherwise IME developers who have overridden those interface methods will lose
@@ -579,8 +566,8 @@
             }
             clearInsetOfPreviousIme();
             // If user uses hard keyboard, IME button should always be shown.
-            mImm.setImeWindowStatus(mToken, mStartInputToken,
-                    mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+            mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()),
+                    mBackDisposition);
             if (resultReceiver != null) {
                 resultReceiver.send(wasVis != isInputViewShown()
                         ? InputMethodManager.RESULT_SHOWN
@@ -1059,8 +1046,8 @@
             }
             // If user uses hard keyboard, IME button should always be shown.
             boolean showing = onEvaluateInputViewShown();
-            mImm.setImeWindowStatus(mToken, mStartInputToken,
-                    IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+            mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
+                    mBackDisposition);
         }
     }
 
@@ -1110,8 +1097,7 @@
             return;
         }
         mBackDisposition = disposition;
-        mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
-                mBackDisposition);
+        mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
     }
 
     /**
@@ -1861,8 +1847,7 @@
 
         final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
         if (previousImeWindowStatus != nextImeWindowStatus) {
-            mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
-                    mBackDisposition);
+            mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
         }
         if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
             if (DEBUG) Log.v(TAG, "showWindow: showing!");
@@ -1887,7 +1872,7 @@
     }
 
     private void doHideWindow() {
-        mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
+        mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
         hideWindow();
     }
 
@@ -2869,7 +2854,6 @@
         p.println("  mInputStarted=" + mInputStarted
                 + " mInputViewStarted=" + mInputViewStarted
                 + " mCandidatesViewStarted=" + mCandidatesViewStarted);
-        p.println("  mStartInputToken=" + mStartInputToken);
 
         if (mInputEditorInfo != null) {
             p.println("  mInputEditorInfo:");
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 1aeac5f..3ca7f77 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -144,7 +144,6 @@
 
     /**
      * Set this incident report privacy policy spec.
-     * @hide
      */
     public void setPrivacyPolicy(int dest) {
         switch (dest) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 63ee624..bb93af5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -798,10 +798,18 @@
     }
 
     /** @hide */
-    public void setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis,
-            int backDisposition) {
+    public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
         try {
-            mService.setImeWindowStatus(imeToken, startInputToken, vis, backDisposition);
+            mService.setImeWindowStatus(imeToken, vis, backDisposition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void reportStartInput(IBinder imeToken, IBinder startInputToken) {
+        try {
+            mService.reportStartInput(imeToken, startInputToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 6c3a58c..989c58b 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -79,6 +79,11 @@
      */
     public static final int ACTION_ROTATE_SCREEN = 6;
 
+    /*
+     * Time between we get a face acquired signal until we start with the unlock animation
+     */
+    public static final int ACTION_FACE_WAKE_AND_UNLOCK = 6;
+
     private static final String[] NAMES = new String[] {
             "expand panel",
             "toggle recents",
@@ -86,7 +91,8 @@
             "check credential",
             "check credential unlocked",
             "turn on screen",
-            "rotate the screen"};
+            "rotate the screen",
+            "face wake-and-unlock" };
 
     private static LatencyTracker sLatencyTracker;
 
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9ed1ffb..f76eddf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -74,8 +74,8 @@
     void hideMySoftInput(in IBinder token, int flags);
     void showMySoftInput(in IBinder token, int flags);
     void updateStatusIcon(in IBinder token, String packageName, int iconId);
-    void setImeWindowStatus(in IBinder token, in IBinder startInputToken, int vis,
-            int backDisposition);
+    void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+    void reportStartInput(in IBinder token, in IBinder startInputToken);
     void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
     boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
     InputMethodSubtype getCurrentInputMethodSubtype();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d294933..80d8063 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1528,7 +1528,7 @@
      * @see StrongAuthTracker#isFingerprintAllowedForUser
      */
     public boolean isFingerprintAllowedForUser(int userId) {
-        return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0;
+        return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0;
     }
 
     public boolean isUserInLockdown(int userId) {
@@ -1733,11 +1733,10 @@
         public static final int STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN = 0x20;
 
         /**
-         * Strong auth flags that do not prevent fingerprint from being accepted as auth.
-         *
-         * If any other flags are set, fingerprint is disabled.
+         * Strong auth flags that do not prevent biometric methods from being accepted as auth.
+         * If any other flags are set, biometric authentication is disabled.
          */
-        private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
+        private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED
                 | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 
         private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
@@ -1784,11 +1783,11 @@
         }
 
         /**
-         * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
-         * current strong authentication requirements.
+         * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+         * by the current strong authentication requirements.
          */
-        public boolean isFingerprintAllowedForUser(int userId) {
-            return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
+        public boolean isBiometricAllowedForUser(int userId) {
+            return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0;
         }
 
         /**
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 12165d4..caf4e4b4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -535,17 +535,209 @@
   return true;
 }
 
+// Utility routine to specialize a zygote child process.
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+                             jint runtime_flags, jobjectArray javaRlimits,
+                             jlong permittedCapabilities, jlong effectiveCapabilities,
+                             jint mount_external, jstring java_se_info, jstring java_se_name,
+                             bool is_system_server, bool is_child_zygote, jstring instructionSet,
+                             jstring dataDir) {
+  std::string error_msg;
+
+  auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
+      __attribute__ ((noreturn)) {
+    const char* se_name_c_str = nullptr;
+    std::unique_ptr<ScopedUtfChars> se_name;
+    if (java_se_name != nullptr) {
+      se_name.reset(new ScopedUtfChars(env, java_se_name));
+      se_name_c_str = se_name->c_str();
+    }
+    if (se_name_c_str == nullptr && is_system_server) {
+      se_name_c_str = "system_server";
+    }
+    const std::string& error_msg = (se_name_c_str == nullptr)
+        ? msg
+        : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
+    env->FatalError(error_msg.c_str());
+    __builtin_unreachable();
+  };
+
+  // Keep capabilities across UID change, unless we're staying root.
+  if (uid != 0) {
+    if (!EnableKeepCapabilities(&error_msg)) {
+      fail_fn(error_msg);
+    }
+  }
+
+  if (!SetInheritable(permittedCapabilities, &error_msg)) {
+    fail_fn(error_msg);
+  }
+  if (!DropCapabilitiesBoundingSet(&error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  bool use_native_bridge = !is_system_server && (instructionSet != NULL)
+      && android::NativeBridgeAvailable();
+  if (use_native_bridge) {
+    ScopedUtfChars isa_string(env, instructionSet);
+    use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
+  }
+  if (use_native_bridge && dataDir == NULL) {
+    // dataDir should never be null if we need to use a native bridge.
+    // In general, dataDir will never be null for normal applications. It can only happen in
+    // special cases (for isolated processes which are not associated with any app). These are
+    // launched by the framework and should not be emulated anyway.
+    use_native_bridge = false;
+    ALOGW("Native bridge will not be used because dataDir == NULL.");
+  }
+
+  if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
+    ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
+    if (errno == ENOTCONN || errno == EROFS) {
+      // When device is actively encrypting, we get ENOTCONN here
+      // since FUSE was mounted before the framework restarted.
+      // When encrypted device is booting, we get EROFS since
+      // FUSE hasn't been created yet by init.
+      // In either case, continue without external storage.
+    } else {
+      fail_fn(error_msg);
+    }
+  }
+
+  // If this zygote isn't root, it won't be able to create a process group,
+  // since the directory is owned by root.
+  if (!is_system_server && getuid() == 0) {
+      int rc = createProcessGroup(uid, getpid());
+      if (rc != 0) {
+          if (rc == -EROFS) {
+              ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+          } else {
+              ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0/*pid*/, strerror(-rc));
+          }
+      }
+  }
+
+  if (!SetGids(env, javaGids, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!SetRLimits(env, javaRlimits, &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (use_native_bridge) {
+    ScopedUtfChars isa_string(env, instructionSet);
+    ScopedUtfChars data_dir(env, dataDir);
+    android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
+  }
+
+  int rc = setresgid(gid, gid, gid);
+  if (rc == -1) {
+    fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+  }
+
+  // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+  // uid from 0, which clears capabilities.  The other alternative is to call
+  // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+  // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
+  // app process.
+  SetUpSeccompFilter(uid);
+
+  rc = setresuid(uid, uid, uid);
+  if (rc == -1) {
+    fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+  }
+
+  // The "dumpable" flag of a process, which controls core dump generation, is
+  // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+  // user or group ID changes. See proc(5) for possible values. In most cases,
+  // the value is 0, so core dumps are disabled for zygote children. However,
+  // when running in a Chrome OS container, the value is already set to 2,
+  // which allows the external crash reporter to collect all core dumps. Since
+  // only system crashes are interested, core dump is disabled for app
+  // processes. This also ensures compliance with CTS.
+  int dumpable = prctl(PR_GET_DUMPABLE);
+  if (dumpable == -1) {
+      ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+      RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+  }
+  if (dumpable == 2 && uid >= AID_APP) {
+    if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+      ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+      RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+    }
+  }
+
+  if (NeedsNoRandomizeWorkaround()) {
+      // Work around ARM kernel ASLR lossage (http://b/5817320).
+      int old_personality = personality(0xffffffff);
+      int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+      if (new_personality == -1) {
+          ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+      }
+  }
+
+  if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
+                       &error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  if (!SetSchedulerPolicy(&error_msg)) {
+    fail_fn(error_msg);
+  }
+
+  const char* se_info_c_str = NULL;
+  ScopedUtfChars* se_info = NULL;
+  if (java_se_info != NULL) {
+      se_info = new ScopedUtfChars(env, java_se_info);
+      se_info_c_str = se_info->c_str();
+      if (se_info_c_str == NULL) {
+        fail_fn("se_info_c_str == NULL");
+      }
+  }
+  const char* se_name_c_str = NULL;
+  ScopedUtfChars* se_name = NULL;
+  if (java_se_name != NULL) {
+      se_name = new ScopedUtfChars(env, java_se_name);
+      se_name_c_str = se_name->c_str();
+      if (se_name_c_str == NULL) {
+        fail_fn("se_name_c_str == NULL");
+      }
+  }
+  rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+  if (rc == -1) {
+    fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+          is_system_server, se_info_c_str, se_name_c_str));
+  }
+
+  // Make it easier to debug audit logs by setting the main thread's name to the
+  // nice name rather than "app_process".
+  if (se_name_c_str == NULL && is_system_server) {
+    se_name_c_str = "system_server";
+  }
+  if (se_name_c_str != NULL) {
+    SetThreadName(se_name_c_str);
+  }
+
+  delete se_info;
+  delete se_name;
+
+  // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+  UnsetChldSignalHandler();
+
+  env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+                            is_system_server, is_child_zygote, instructionSet);
+  if (env->ExceptionCheck()) {
+    fail_fn("Error calling post fork hooks.");
+  }
+}
+
 // 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 runtime_flags, jobjectArray javaRlimits,
-                                     jlong permittedCapabilities, jlong effectiveCapabilities,
-                                     jint mount_external,
-                                     jstring java_se_info, jstring java_se_name,
-                                     bool is_system_server, jintArray fdsToClose,
-                                     jintArray fdsToIgnore, bool is_child_zygote,
-                                     jstring instructionSet, jstring dataDir) {
+static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
+                        jintArray fdsToClose, jintArray fdsToIgnore) {
   SetSignalHandlers();
 
+  // Block SIGCHLD prior to fork.
   sigset_t sigchld;
   sigemptyset(&sigchld);
   sigaddset(&sigchld, SIGCHLD);
@@ -602,6 +794,7 @@
   pid_t pid = fork();
 
   if (pid == 0) {
+    // The child process.
     PreApplicationInit();
 
     // Clean up any descriptors which must be closed immediately
@@ -614,187 +807,11 @@
     if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
       fail_fn(error_msg);
     }
+  }
 
-    if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
-      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
-    }
-
-    // Keep capabilities across UID change, unless we're staying root.
-    if (uid != 0) {
-      if (!EnableKeepCapabilities(&error_msg)) {
-        fail_fn(error_msg);
-      }
-    }
-
-    if (!SetInheritable(permittedCapabilities, &error_msg)) {
-      fail_fn(error_msg);
-    }
-    if (!DropCapabilitiesBoundingSet(&error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    bool use_native_bridge = !is_system_server && (instructionSet != NULL)
-        && android::NativeBridgeAvailable();
-    if (use_native_bridge) {
-      ScopedUtfChars isa_string(env, instructionSet);
-      use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
-    }
-    if (use_native_bridge && dataDir == NULL) {
-      // dataDir should never be null if we need to use a native bridge.
-      // In general, dataDir will never be null for normal applications. It can only happen in
-      // special cases (for isolated processes which are not associated with any app). These are
-      // launched by the framework and should not be emulated anyway.
-      use_native_bridge = false;
-      ALOGW("Native bridge will not be used because dataDir == NULL.");
-    }
-
-    if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
-      ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
-      if (errno == ENOTCONN || errno == EROFS) {
-        // When device is actively encrypting, we get ENOTCONN here
-        // since FUSE was mounted before the framework restarted.
-        // When encrypted device is booting, we get EROFS since
-        // FUSE hasn't been created yet by init.
-        // In either case, continue without external storage.
-      } else {
-        fail_fn(error_msg);
-      }
-    }
-
-    // If this zygote isn't root, it won't be able to create a process group,
-    // since the directory is owned by root.
-    if (!is_system_server && getuid() == 0) {
-        int rc = createProcessGroup(uid, getpid());
-        if (rc != 0) {
-            if (rc == -EROFS) {
-                ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
-            } else {
-                ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
-            }
-        }
-    }
-
-    std::string error_msg;
-    if (!SetGids(env, javaGids, &error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    if (!SetRLimits(env, javaRlimits, &error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    if (use_native_bridge) {
-      ScopedUtfChars isa_string(env, instructionSet);
-      ScopedUtfChars data_dir(env, dataDir);
-      android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
-    }
-
-    int rc = setresgid(gid, gid, gid);
-    if (rc == -1) {
-      fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
-    }
-
-    // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
-    // uid from 0, which clears capabilities.  The other alternative is to call
-    // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
-    // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
-    // app process.
-    SetUpSeccompFilter(uid);
-
-    rc = setresuid(uid, uid, uid);
-    if (rc == -1) {
-      fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
-    }
-
-    // The "dumpable" flag of a process, which controls core dump generation, is
-    // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
-    // user or group ID changes. See proc(5) for possible values. In most cases,
-    // the value is 0, so core dumps are disabled for zygote children. However,
-    // when running in a Chrome OS container, the value is already set to 2,
-    // which allows the external crash reporter to collect all core dumps. Since
-    // only system crashes are interested, core dump is disabled for app
-    // processes. This also ensures compliance with CTS.
-    int dumpable = prctl(PR_GET_DUMPABLE);
-    if (dumpable == -1) {
-        ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
-        RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
-    }
-    if (dumpable == 2 && uid >= AID_APP) {
-      if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
-        ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
-        RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
-      }
-    }
-
-    if (NeedsNoRandomizeWorkaround()) {
-        // Work around ARM kernel ASLR lossage (http://b/5817320).
-        int old_personality = personality(0xffffffff);
-        int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
-        if (new_personality == -1) {
-            ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
-        }
-    }
-
-    if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
-                         &error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    if (!SetSchedulerPolicy(&error_msg)) {
-      fail_fn(error_msg);
-    }
-
-    const char* se_info_c_str = NULL;
-    ScopedUtfChars* se_info = NULL;
-    if (java_se_info != NULL) {
-        se_info = new ScopedUtfChars(env, java_se_info);
-        se_info_c_str = se_info->c_str();
-        if (se_info_c_str == NULL) {
-          fail_fn("se_info_c_str == NULL");
-        }
-    }
-    const char* se_name_c_str = NULL;
-    ScopedUtfChars* se_name = NULL;
-    if (java_se_name != NULL) {
-        se_name = new ScopedUtfChars(env, java_se_name);
-        se_name_c_str = se_name->c_str();
-        if (se_name_c_str == NULL) {
-          fail_fn("se_name_c_str == NULL");
-        }
-    }
-    rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
-    if (rc == -1) {
-      fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
-            is_system_server, se_info_c_str, se_name_c_str));
-    }
-
-    // Make it easier to debug audit logs by setting the main thread's name to the
-    // nice name rather than "app_process".
-    if (se_name_c_str == NULL && is_system_server) {
-      se_name_c_str = "system_server";
-    }
-    if (se_name_c_str != NULL) {
-      SetThreadName(se_name_c_str);
-    }
-
-    delete se_info;
-    delete se_name;
-
-    // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
-    UnsetChldSignalHandler();
-
-    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
-                              is_system_server, is_child_zygote, instructionSet);
-    if (env->ExceptionCheck()) {
-      fail_fn("Error calling post fork hooks.");
-    }
-  } else if (pid > 0) {
-    // the parent process
-
-    // We blocked SIGCHLD prior to a fork, we unblock it here.
-    if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
-      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
-    }
+  // We blocked SIGCHLD prior to a fork, we unblock it here.
+  if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
+    fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
   }
   return pid;
 }
@@ -881,22 +898,27 @@
     // available.
     capabilities &= GetEffectiveCapabilityMask(env);
 
-    return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
-            rlimits, capabilities, capabilities, mount_external, se_info,
-            se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
-            instructionSet, appDataDir);
+    pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
+    if (pid == 0) {
+      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+                       capabilities, capabilities,
+                       mount_external, se_info, se_name, false,
+                       is_child_zygote == JNI_TRUE, instructionSet, appDataDir);
+    }
+    return pid;
 }
 
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
         JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
         jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
         jlong effectiveCapabilities) {
-  pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
-                                      runtime_flags, rlimits,
-                                      permittedCapabilities, effectiveCapabilities,
-                                      MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
-                                      NULL, false, NULL, NULL);
-  if (pid > 0) {
+  pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
+  if (pid == 0) {
+      SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+                       permittedCapabilities, effectiveCapabilities,
+                       MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
+                       false, NULL, NULL);
+  } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
       gSystemServerPid = pid;
diff --git a/core/proto/android/service/face.proto b/core/proto/android/service/face.proto
new file mode 100644
index 0000000..4188a34
--- /dev/null
+++ b/core/proto/android/service/face.proto
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto3";
+
+package android.service.face;
+
+option java_multiple_files = true;
+option java_outer_classname = "FaceServiceProto";
+
+message FaceServiceDumpProto {
+    // Each log may include multiple user_id for different users.
+    repeated FaceUserStatsProto users = 1;
+}
+
+message FaceUserStatsProto {
+    // Refer to the UserHandle documentation.
+    int32 user_id = 1;
+
+    // Normal face authentications stats (e.g. lockscreen).
+    FaceActionStatsProto normal = 2;
+
+    // Crypto authentications (e.g. to unlock password storage, make secure
+    // purchases, etc).
+    FaceActionStatsProto crypto = 3;
+}
+
+message FaceActionStatsProto {
+    // Number of accepted faces.
+    int32 accept = 1;
+
+    // Number of rejected faces.
+    int32 reject = 2;
+
+    // Total number of acquisitions. Should be >= accept+reject due to poor
+    // image acquisition in some cases (too high, too low, poor gaze, etc.)
+    int32 acquire = 3;
+
+    // Total number of lockouts.
+    int32 lockout = 4;
+
+    // Total number of permanent lockouts.
+    int32 lockout_permanent = 5;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c601215..f4b7ec8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -600,6 +600,7 @@
     <protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" />
     <protected-broadcast android:name="android.app.action.STATSD_STARTED" />
     <protected-broadcast android:name="com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET" />
+    <protected-broadcast android:name="com.android.server.biometrics.face.ACTION_LOCKOUT_RESET" />
 
     <!-- For IdleController -->
     <protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
@@ -3637,6 +3638,14 @@
     <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
         android:protectionLevel="signature" />
 
+    <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
+    <permission android:name="android.permission.MANAGE_FACE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
+    <permission android:name="android.permission.RESET_FACE_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/strings.xml b/core/res/res/values/strings.xml
index cf68934..4e96671 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1408,6 +1408,74 @@
     <!-- Content description which should be used for the fingerprint icon. -->
     <string name="fingerprint_icon_content_description">Fingerprint icon</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
+    <string name="permlab_manageFace">manage face authentication hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
+    <string name="permdesc_manageFace">Allows the app to invoke methods to add and delete facial templates for use.</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
+    <string name="permlab_useFaceAuthentication">use face authentication hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
+    <string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
+
+    <!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
+    <string name="face_acquired_insufficient">Couldn\u2019t process face. Please try again.</string>
+    <!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_bright">Face is too bright. Please try in lower light.</string>
+    <!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_dark">Face is too dark. Please uncover light source.</string>
+    <!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_close">Please move sensor farther from face.</string>
+    <!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_far">Please bring sensor closer to face.</string>
+    <!-- Message shown during face acquisition when the user is too high relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_high">Please move sensor higher.</string>
+    <!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_low">Please move sensor lower.</string>
+    <!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_right">Please move sensor to the right.</string>
+    <!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_too_left">Please move sensor to the left.</string>
+    <!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
+    <string name="face_acquired_poor_gaze">Please look at the sensor.</string>
+    <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
+    <string name="face_acquired_not_detected">No face detected.</string>
+
+
+    <!-- Message shown during face acquisition when the face is not kept steady infront of device [CHAR LIMIT=50] -->
+    <string name="face_acquired_not_steady">Keep face steady infront of device.</string>
+    <!-- Array containing custom messages shown during face acquisition from vendor.  Vendor is expected to add and translate these strings -->
+    <string-array name="face_acquired_vendor">
+    </string-array>
+
+    <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=50] -->
+    <string name="face_error_hw_not_available">Face hardware not available.</string>
+    <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
+    <string name="face_error_timeout">Face time out reached. Try again.</string>
+    <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=50] -->
+    <string name="face_error_no_space">Face can\u2019t be stored.</string>
+    <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
+    <string name="face_error_canceled">Face operation canceled.</string>
+    <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
+    <string name="face_error_lockout">Too many attempts. Try again later.</string>
+    <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
+    <string name="face_error_lockout_permanent">Too many attempts. Facial authentication disabled.</string>
+    <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
+    <string name="face_error_unable_to_process">Try again.</string>
+    <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
+    <string name="face_error_not_enrolled">No face enrolled.</string>
+    <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
+    <string name="face_error_hw_not_present">This device does not have a face authentication sensor</string>
+
+    <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
+    <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
+
+    <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
+    <string-array name="face_error_vendor">
+    </string-array>
+
+    <!-- Content description which should be used for the face icon. [CHAR LIMIT=10] -->
+    <string name="face_icon_content_description">Face icon</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSyncSettings">read sync settings</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3180,6 +3248,21 @@
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
     <string name="wifi_no_internet_detailed">Tap for options</string>
 
+    <!-- A notification is shown when the user's softap config has been changed due to underlying
+         hardware restrictions. This is the notifications's title.
+         [CHAR_LIMIT=NONE] -->
+    <string name="wifi_softap_config_change">Changes to your hotspot settings</string>
+
+    <!-- A notification is shown when the user's softap config has been changed due to underlying
+         hardware restrictions. This is the notification's summary message.
+         [CHAR_LIMIT=NONE] -->
+    <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string>
+
+    <!-- A notification is shown when the user's softap config has been changed due to underlying
+         hardware restrictions. This is the notification's full message.
+         [CHAR_LIMIT=NONE] -->
+    <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string>
+
     <!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's title. %1$s is the network type that the device switched to, e.g., cellular data. It is one of the strings in the network_switch_type_name array. -->
     <string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d9642cb..c746f47 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1061,6 +1061,9 @@
   <java-symbol type="string" name="network_switch_type_name_unknown" />
   <java-symbol type="string" name="wifi_no_internet" />
   <java-symbol type="string" name="wifi_no_internet_detailed" />
+  <java-symbol type="string" name="wifi_softap_config_change" />
+  <java-symbol type="string" name="wifi_softap_config_change_summary" />
+  <java-symbol type="string" name="wifi_softap_config_change_detailed" />
   <java-symbol type="string" name="wifi_connect_alert_title" />
   <java-symbol type="string" name="wifi_connect_alert_message" />
   <java-symbol type="string" name="wifi_connect_default_application" />
@@ -2420,6 +2423,30 @@
   <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
   <java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
 
+  <!-- Face authentication messages -->
+  <java-symbol type="string" name="face_error_unable_to_process" />
+  <java-symbol type="string" name="face_error_hw_not_available" />
+  <java-symbol type="string" name="face_error_no_space" />
+  <java-symbol type="string" name="face_error_timeout" />
+  <java-symbol type="array" name="face_error_vendor" />
+  <java-symbol type="string" name="face_error_canceled" />
+  <java-symbol type="string" name="face_error_lockout" />
+  <java-symbol type="string" name="face_error_lockout_permanent" />
+  <java-symbol type="string" name="face_error_not_enrolled" />
+  <java-symbol type="string" name="face_error_hw_not_present" />
+  <java-symbol type="string" name="face_acquired_insufficient" />
+  <java-symbol type="string" name="face_acquired_too_bright" />
+  <java-symbol type="string" name="face_acquired_too_dark" />
+  <java-symbol type="string" name="face_acquired_too_close" />
+  <java-symbol type="string" name="face_acquired_too_far" />
+  <java-symbol type="string" name="face_acquired_too_high" />
+  <java-symbol type="string" name="face_acquired_too_low" />
+  <java-symbol type="string" name="face_acquired_too_right" />
+  <java-symbol type="string" name="face_acquired_too_left" />
+  <java-symbol type="string" name="face_acquired_poor_gaze" />
+  <java-symbol type="string" name="face_acquired_not_detected" />
+  <java-symbol type="array" name="face_acquired_vendor" />
+
   <!-- From various Material changes -->
   <java-symbol type="attr" name="titleTextAppearance" />
   <java-symbol type="attr" name="subtitleTextAppearance" />
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 2a1b36f..9a72706 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -79,7 +79,7 @@
     void initialize();
 
     // Quick check to see if the VulkanManager has been initialized.
-    bool hasVkContext() { return mBackendContext.get() != nullptr; }
+    bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
 
     // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
     // VulkanSurface object which is returned.
@@ -188,8 +188,6 @@
 
     RenderThread& mRenderThread;
 
-    sk_sp<const GrVkBackendContext> mBackendContext;
-
     VkInstance mInstance = VK_NULL_HANDLE;
     VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
     VkDevice mDevice = VK_NULL_HANDLE;
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 72647ab..617e49a 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -18,7 +18,6 @@
     <string name="app_name">Android Services Library</string>
 
     <string name="notification_assistant">Notification Assistant</string>
-    <string name="prompt_block_reason">Too many dismissals:views</string>
 
     <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
     <string-array name="autofill_field_classification_available_algorithms">
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f878822..fdd6a9c 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -292,8 +292,7 @@
         if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
         Bundle signals = new Bundle();
         signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
-        return new Adjustment(packageName, key,  signals,
-                getContext().getString(R.string.prompt_block_reason), user);
+        return new Adjustment(packageName, key,  signals, "", user);
     }
 
     // for testing
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f728684..9d398b5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -841,7 +841,19 @@
             WifiConfiguration config = WifiConfiguration
                     .getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
             if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
+            int originalApBand = config.apBand;
             mWifiManager.setWifiApConfiguration(config);
+
+            // Depending on device hardware, we may need to notify the user of a setting change for
+            // the apBand preference
+            boolean dualMode = mWifiManager.isDualModeSupported();
+            int storedApBand = mWifiManager.getWifiApConfiguration().apBand;
+            if (dualMode) {
+                if (storedApBand != originalApBand) {
+                    Log.d(TAG, "restored ap configuration requires a conversion, notify the user");
+                    mWifiManager.notifyUserOfApBandConversion();
+                }
+            }
         } catch (IOException | BackupUtils.BadVersionException e) {
             Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
         }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index beb4e9e..eab4b97 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -120,6 +120,7 @@
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <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_BIOMETRIC" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
     <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 513d848..6bc0965 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -381,7 +381,10 @@
     </plurals>
 
     <!-- Fingerprint hint message when finger was not recognized.-->
-    <string name="fingerprint_not_recognized">Not recognized</string>
+    <string name="kg_fingerprint_not_recognized">Not recognized</string>
+
+    <!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
+    <string name="kg_face_not_recognized">Not recognized</string>
 
     <!-- Instructions telling the user remaining times when enter SIM PIN view.  -->
     <plurals name="kg_password_default_pin_message">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index cd831d1..d38cc0f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -54,6 +54,7 @@
     public static final int HIT_TARGET_HOME = 2;
     public static final int HIT_TARGET_OVERVIEW = 3;
     public static final int HIT_TARGET_ROTATION = 4;
+    public static final int HIT_TARGET_DEAD_ZONE = 5;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({FLAG_DISABLE_SWIPE_UP,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 62b8e7c..0340904 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -27,5 +27,5 @@
      */
     public static final boolean DEBUG = false;
     public static final boolean DEBUG_SIM_STATES = true;
-    public static final boolean DEBUG_FP_WAKELOCK = true;
+    public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 075faa1..d639fbf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -39,6 +39,7 @@
 import android.app.UserSwitchObserver;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -48,6 +49,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
+import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -72,7 +74,6 @@
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
@@ -145,26 +146,26 @@
     private static final int MSG_DREAMING_STATE_CHANGED = 333;
     private static final int MSG_USER_UNLOCKED = 334;
     private static final int MSG_ASSISTANT_STACK_CHANGED = 335;
-    private static final int MSG_FINGERPRINT_AUTHENTICATION_CONTINUE = 336;
+    private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
     private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
 
-    /** Fingerprint state: Not listening to fingerprint. */
-    private static final int FINGERPRINT_STATE_STOPPED = 0;
+    /** Biometric authentication state: Not listening. */
+    private static final int BIOMETRIC_STATE_STOPPED = 0;
 
-    /** Fingerprint state: Listening. */
-    private static final int FINGERPRINT_STATE_RUNNING = 1;
+    /** Biometric authentication state: Listening. */
+    private static final int BIOMETRIC_STATE_RUNNING = 1;
 
     /**
-     * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to
+     * Biometric authentication: Cancelling and waiting for the relevant biometric service to
      * send us the confirmation that cancellation has happened.
      */
-    private static final int FINGERPRINT_STATE_CANCELLING = 2;
+    private static final int BIOMETRIC_STATE_CANCELLING = 2;
 
     /**
-     * Fingerprint state: During cancelling we got another request to start listening, so when we
+     * Biometric 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 final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
 
     private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
 
@@ -227,7 +228,8 @@
     private List<SubscriptionInfo> mSubscriptionInfo;
     private TrustManager mTrustManager;
     private UserManager mUserManager;
-    private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
+    private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+    private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
     private LockPatternUtils mLockPatternUtils;
     private final IDreamManager mDreamManager;
     private boolean mIsDreaming;
@@ -235,14 +237,15 @@
     private boolean mLogoutEnabled;
 
     /**
-     * Short delay before restarting fingerprint authentication after a successful try
-     * This should be slightly longer than the time between onFingerprintAuthenticated and
-     * setKeyguardGoingAway(true).
+     * Short delay before restarting biometric authentication after a successful try
+     * This should be slightly longer than the time between on<biometric>Authenticated
+     * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
      */
-    private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
+    private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
 
     // If FP daemon dies, keyguard should retry after a short delay
-    private int mHardwareUnavailableRetryCount = 0;
+    private int mHardwareFingerprintUnavailableRetryCount = 0;
+    private int mHardwareFaceUnavailableRetryCount = 0;
     private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
     private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
 
@@ -333,10 +336,10 @@
                     break;
                 case MSG_ASSISTANT_STACK_CHANGED:
                     mAssistantVisible = (boolean)msg.obj;
-                    updateFingerprintListeningState();
+                    updateBiometricListeningState();
                     break;
-                case MSG_FINGERPRINT_AUTHENTICATION_CONTINUE:
-                    updateFingerprintListeningState();
+                case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
+                    updateBiometricListeningState();
                     break;
                 case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
                     updateLogoutEnabled();
@@ -359,10 +362,11 @@
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
     private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
     private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
+    private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
     private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
 
     private static int sCurrentUser;
-    private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
+    private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
     private static boolean sDisableHandlerCheckForTesting;
 
     public synchronized static void setCurrentUser(int currentUser) {
@@ -487,7 +491,7 @@
      */
     public void setKeyguardOccluded(boolean occluded) {
         mKeyguardOccluded = occluded;
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -515,19 +519,19 @@
         mUserFingerprintAuthenticated.put(userId, true);
         // Update/refresh trust state only if user can skip bouncer
         if (getUserCanSkipBouncer(userId)) {
-            mTrustManager.unlockedByFingerprintForUser(userId);
+            mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
         }
         // Don't send cancel if authentication succeeds
         mFingerprintCancelSignal = null;
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAuthenticated(userId);
+                cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
             }
         }
 
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE),
-                FINGERPRINT_CONTINUE_DELAY_MS);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+                BIOMETRIC_CONTINUE_DELAY_MS);
 
         // Only authenticate fingerprint once when assistant is visible
         mAssistantVisible = false;
@@ -539,10 +543,10 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAuthFailed();
+                cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
             }
         }
-        handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
+        handleFingerprintHelp(-1, mContext.getString(R.string.kg_fingerprint_not_recognized));
     }
 
     private void handleFingerprintAcquired(int acquireInfo) {
@@ -552,7 +556,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAcquired();
+                cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
             }
         }
     }
@@ -577,7 +581,7 @@
             }
             onFingerprintAuthenticated(userId);
         } finally {
-            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+            setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
         }
         Trace.endSection();
     }
@@ -586,7 +590,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintHelp(msgId, helpString);
+                cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT);
             }
         }
     }
@@ -595,23 +599,23 @@
         @Override
         public void run() {
             Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
-                    mHardwareUnavailableRetryCount);
+                    mHardwareFingerprintUnavailableRetryCount);
             updateFingerprintListeningState();
         }
     };
 
     private void handleFingerprintError(int msgId, String errString) {
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
-                && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
-            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+                && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
             startListeningForFingerprint();
         } else {
-            setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+            setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
-            if (mHardwareUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
-                mHardwareUnavailableRetryCount++;
+            if (mHardwareFingerprintUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+                mHardwareFingerprintUnavailableRetryCount++;
                 mHandler.removeCallbacks(mRetryFingerprintAuthentication);
                 mHandler.postDelayed(mRetryFingerprintAuthentication, HW_UNAVAILABLE_TIMEOUT);
             }
@@ -626,7 +630,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintError(msgId, errString);
+                cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
             }
         }
     }
@@ -636,8 +640,8 @@
     }
 
     private void setFingerprintRunningState(int fingerprintRunningState) {
-        boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
-        boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+        boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+        boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         mFingerprintRunningState = fingerprintRunningState;
 
         // Clients of KeyguardUpdateMonitor don't care about the internal state about the
@@ -653,10 +657,162 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning());
+                cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(),
+                        BiometricSourceType.FINGERPRINT);
             }
         }
     }
+
+    private void onFaceAuthenticated(int userId) {
+        Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
+        mUserFaceAuthenticated.put(userId, true);
+        // Update/refresh trust state only if user can skip bouncer
+        if (getUserCanSkipBouncer(userId)) {
+            mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
+        }
+        // Don't send cancel if authentication succeeds
+        mFaceCancelSignal = null;
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricAuthenticated(userId,
+                        BiometricSourceType.FACE);
+            }
+        }
+
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+                BIOMETRIC_CONTINUE_DELAY_MS);
+
+        // Only authenticate face once when assistant is visible
+        mAssistantVisible = false;
+
+        Trace.endSection();
+    }
+
+    private void handleFaceAuthFailed() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricAuthFailed(BiometricSourceType.FACE);
+            }
+        }
+        handleFaceHelp(-1, mContext.getString(R.string.kg_face_not_recognized));
+    }
+
+    private void handleFaceAcquired(int acquireInfo) {
+        if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
+            return;
+        }
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricAcquired(BiometricSourceType.FACE);
+            }
+        }
+    }
+
+    private void handleFaceAuthenticated(int authUserId) {
+        Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
+        try {
+            final int userId;
+            try {
+                userId = ActivityManager.getService().getCurrentUser().id;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to get current user id: ", e);
+                return;
+            }
+            if (userId != authUserId) {
+                Log.d(TAG, "Face authenticated for wrong user: " + authUserId);
+                return;
+            }
+            if (isFaceDisabled(userId)) {
+                Log.d(TAG, "Face authentication disabled by DPM for userId: " + userId);
+                return;
+            }
+            onFaceAuthenticated(userId);
+        } finally {
+            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+        }
+        Trace.endSection();
+    }
+
+    private void handleFaceHelp(int msgId, String helpString) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE);
+            }
+        }
+    }
+
+    private Runnable mRetryFaceAuthentication = new Runnable() {
+        @Override
+        public void run() {
+            Log.w(TAG, "Retrying face after HW unavailable, attempt " +
+                    mHardwareFaceUnavailableRetryCount);
+            updateFaceListeningState();
+        }
+    };
+
+    private void handleFaceError(int msgId, String errString) {
+        if (msgId == FaceManager.FACE_ERROR_CANCELED
+                && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+            startListeningForFace();
+        } else {
+            setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+        }
+
+        if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE) {
+            if (mHardwareFaceUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+                mHardwareFaceUnavailableRetryCount++;
+                mHandler.removeCallbacks(mRetryFaceAuthentication);
+                mHandler.postDelayed(mRetryFaceAuthentication, HW_UNAVAILABLE_TIMEOUT);
+            }
+        }
+
+        if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+            mLockPatternUtils.requireStrongAuth(
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+                    getCurrentUser());
+        }
+
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricError(msgId, errString,
+                        BiometricSourceType.FACE);
+            }
+        }
+    }
+
+    private void handleFaceLockoutReset() {
+        updateFaceListeningState();
+    }
+
+    private void setFaceRunningState(int faceRunningState) {
+        boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING;
+        boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING;
+        mFaceRunningState = faceRunningState;
+
+        // Clients of KeyguardUpdateMonitor don't care about the internal state or about the
+        // asynchronousness of the cancel cycle. So only notify them if the actualy running state
+        // has changed.
+        if (wasRunning != isRunning) {
+            notifyFaceRunningStateChanged();
+        }
+    }
+
+    private void notifyFaceRunningStateChanged() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
+                        BiometricSourceType.FACE);
+            }
+        }
+    }
+
     private void handleFaceUnlockStateChanged(boolean running, int userId) {
         checkIsHandlerThread();
         mUserFaceUnlockRunning.put(userId, running);
@@ -673,7 +829,11 @@
     }
 
     public boolean isFingerprintDetectionRunning() {
-        return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+        return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+    }
+
+    public boolean isFaceDetectionRunning() {
+        return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
     }
 
     private boolean isTrustDisabled(int userId) {
@@ -692,9 +852,18 @@
                 || isSimPinSecure();
     }
 
+    private boolean isFaceDisabled(int userId) {
+        final DevicePolicyManager dpm =
+                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId)
+                & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0
+                || isSimPinSecure();
+    }
+
+
     public boolean getUserCanSkipBouncer(int userId) {
         return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
-                && isUnlockingWithFingerprintAllowed());
+                && isUnlockingWithBiometricAllowed());
     }
 
     public boolean getUserHasTrust(int userId) {
@@ -705,8 +874,8 @@
         return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
     }
 
-    public boolean isUnlockingWithFingerprintAllowed() {
-        return mStrongAuthTracker.isUnlockingWithFingerprintAllowed();
+    public boolean isUnlockingWithBiometricAllowed() {
+        return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
     }
 
     public boolean isUserInLockdown(int userId) {
@@ -862,7 +1031,7 @@
         }
     };
 
-    private final FingerprintManager.LockoutResetCallback mLockoutResetCallback
+    private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
             = new FingerprintManager.LockoutResetCallback() {
         @Override
         public void onLockoutReset() {
@@ -870,13 +1039,21 @@
         }
     };
 
-    private FingerprintManager.AuthenticationCallback mAuthenticationCallback
+    private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
+            = new FaceManager.LockoutResetCallback() {
+        @Override
+        public void onLockoutReset() {
+            handleFaceLockoutReset();
+        }
+    };
+
+    private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
             = new AuthenticationCallback() {
 
         @Override
         public void onAuthenticationFailed() {
             handleFingerprintAuthFailed();
-        };
+        }
 
         @Override
         public void onAuthenticationSucceeded(AuthenticationResult result) {
@@ -900,8 +1077,42 @@
             handleFingerprintAcquired(acquireInfo);
         }
     };
+
+    private FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+            = new FaceManager.AuthenticationCallback() {
+
+        @Override
+        public void onAuthenticationFailed() {
+            handleFaceAuthFailed();
+        }
+
+        @Override
+        public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
+            Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
+            handleFaceAuthenticated(result.getUserId());
+            Trace.endSection();
+        }
+
+        @Override
+        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+            handleFaceHelp(helpMsgId, helpString.toString());
+        }
+
+        @Override
+        public void onAuthenticationError(int errMsgId, CharSequence errString) {
+            handleFaceError(errMsgId, errString.toString());
+        }
+
+        @Override
+        public void onAuthenticationAcquired(int acquireInfo) {
+            handleFaceAcquired(acquireInfo);
+        }
+    };
+
     private CancellationSignal mFingerprintCancelSignal;
+    private CancellationSignal mFaceCancelSignal;
     private FingerprintManager mFpm;
+    private FaceManager mFaceAuthenticationManager;
 
     /**
      * When we receive a
@@ -1049,9 +1260,9 @@
             super(context);
         }
 
-        public boolean isUnlockingWithFingerprintAllowed() {
+        public boolean isUnlockingWithBiometricAllowed() {
             int userId = getCurrentUser();
-            return isFingerprintAllowedForUser(userId);
+            return isBiometricAllowedForUser(userId);
         }
 
         public boolean hasUserAuthenticatedSinceBoot() {
@@ -1075,7 +1286,7 @@
 
     protected void handleStartedWakingUp() {
         Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1087,7 +1298,7 @@
     }
 
     protected void handleStartedGoingToSleep(int arg1) {
-        clearFingerprintRecognized();
+        clearBiometricRecognized();
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1096,7 +1307,7 @@
             }
         }
         mGoingToSleep = true;
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     protected void handleFinishedGoingToSleep(int arg1) {
@@ -1108,7 +1319,7 @@
                 cb.onFinishedGoingToSleep(arg1);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     private void handleScreenTurnedOn() {
@@ -1122,7 +1333,8 @@
     }
 
     private void handleScreenTurnedOff() {
-        mHardwareUnavailableRetryCount = 0;
+        mHardwareFingerprintUnavailableRetryCount = 0;
+        mHardwareFaceUnavailableRetryCount = 0;
         final int count = mCallbacks.size();
         for (int i = 0; i < count; i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1141,7 +1353,7 @@
                 cb.onDreamingStateChanged(mIsDreaming);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     private void handleUserInfoChanged(int userId) {
@@ -1238,9 +1450,17 @@
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
             mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
         }
-        updateFingerprintListeningState();
+
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            mFaceAuthenticationManager =
+                    (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+        }
+        updateBiometricListeningState();
         if (mFpm != null) {
-            mFpm.addLockoutResetCallback(mLockoutResetCallback);
+            mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
+        }
+        if (mFaceAuthenticationManager != null) {
+            mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
         }
 
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -1249,28 +1469,55 @@
         mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
     }
 
+    private void updateBiometricListeningState() {
+        updateFingerprintListeningState();
+        updateFaceListeningState();
+    }
+
     private void updateFingerprintListeningState() {
         // If this message exists, we should not authenticate again until this message is
         // consumed by the handler
-        if (mHandler.hasMessages(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE)) {
+        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
             return;
         }
         mHandler.removeCallbacks(mRetryFingerprintAuthentication);
         boolean shouldListenForFingerprint = shouldListenForFingerprint();
-        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
+        if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
             stopListeningForFingerprint();
-        } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
+        } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
                 && shouldListenForFingerprint) {
             startListeningForFingerprint();
         }
     }
 
+    private void updateFaceListeningState() {
+        // If this message exists, we should not authenticate again until this message is
+        // consumed by the handler
+        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+            return;
+        }
+        mHandler.removeCallbacks(mRetryFaceAuthentication);
+        boolean shouldListenForFace = shouldListenForFace();
+        if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
+            stopListeningForFace();
+        } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING
+                && shouldListenForFace) {
+            startListeningForFace();
+        }
+    }
+
     private boolean shouldListenForFingerprintAssistant() {
         return mAssistantVisible && mKeyguardOccluded
                 && !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
                 && !mUserHasTrust.get(getCurrentUser(), false);
     }
 
+    private boolean shouldListenForFaceAssistant() {
+        return mAssistantVisible && mKeyguardOccluded
+                && !mUserFaceAuthenticated.get(getCurrentUser(), false)
+                && !mUserHasTrust.get(getCurrentUser(), false);
+    }
+
     private boolean shouldListenForFingerprint() {
         return (mKeyguardIsVisible || !mDeviceInteractive ||
                 (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
@@ -1279,9 +1526,18 @@
                 && !mKeyguardGoingAway;
     }
 
+    private boolean shouldListenForFace() {
+        return (mKeyguardIsVisible || !mDeviceInteractive ||
+                (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
+                shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
+                && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
+                && !mKeyguardGoingAway;
+    }
+
+
     private void startListeningForFingerprint() {
-        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
-            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
+        if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
+            setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
         }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
@@ -1291,9 +1547,27 @@
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
-            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null,
-                    userId);
-            setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
+            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
+                    null, userId);
+            setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
+        }
+    }
+
+    private void startListeningForFace() {
+        if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
+            setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
+            return;
+        }
+        if (DEBUG) Log.v(TAG, "startListeningForFace()");
+        int userId = ActivityManager.getCurrentUser();
+        if (isUnlockWithFacePossible(userId)) {
+            if (mFaceCancelSignal != null) {
+                mFaceCancelSignal.cancel();
+            }
+            mFaceCancelSignal = new CancellationSignal();
+            mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+                    mFaceAuthenticationCallback, null, userId);
+            setFaceRunningState(BIOMETRIC_STATE_RUNNING);
         }
     }
 
@@ -1302,17 +1576,37 @@
                 && mFpm.getEnrolledFingerprints(userId).size() > 0;
     }
 
+    public boolean isUnlockWithFacePossible(int userId) {
+        return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+                && !isFaceDisabled(userId)
+                && mFaceAuthenticationManager.hasEnrolledFace(userId);
+    }
+
     private void stopListeningForFingerprint() {
         if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
-        if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
+        if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING) {
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
                 mFingerprintCancelSignal = null;
             }
-            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+            setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
         }
-        if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
-            setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+        if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
+        }
+    }
+
+    private void stopListeningForFace() {
+        if (DEBUG) Log.v(TAG, "stopListeningForFace()");
+        if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) {
+            if (mFaceCancelSignal != null) {
+                mFaceCancelSignal.cancel();
+                mFaceCancelSignal = null;
+            }
+            setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
+        }
+        if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
         }
     }
 
@@ -1609,7 +1903,7 @@
                 cb.onKeyguardVisibilityChangedRaw(showing);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -1617,7 +1911,7 @@
      */
     private void handleKeyguardReset() {
         if (DEBUG) Log.d(TAG, "handleKeyguardReset");
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
         mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
     }
 
@@ -1646,7 +1940,7 @@
                 cb.onKeyguardBouncerChanged(isBouncer);
             }
         }
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -1729,7 +2023,7 @@
     public void setSwitchingUser(boolean switching) {
         mSwitchingUser = switching;
         // Since this comes in on a binder thread, we need to post if first
-        mHandler.post(mUpdateFingerprintListeningState);
+        mHandler.post(mUpdateBiometricListeningState);
     }
 
     private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
@@ -1817,9 +2111,11 @@
         mFailedAttempts.put(userId, getFailedUnlockAttempts(userId) + 1);
     }
 
-    public void clearFingerprintRecognized() {
+    public void clearBiometricRecognized() {
         mUserFingerprintAuthenticated.clear();
-        mTrustManager.clearAllFingerprints();
+        mUserFaceAuthenticated.clear();
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE);
     }
 
     public boolean isSimPinVoiceSecure() {
@@ -2054,7 +2350,7 @@
             final int userId = ActivityManager.getCurrentUser();
             final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
             pw.println("  Fingerprint state (user=" + userId + ")");
-            pw.println("    allowed=" + isUnlockingWithFingerprintAllowed());
+            pw.println("    allowed=" + isUnlockingWithBiometricAllowed());
             pw.println("    auth'd=" + mUserFingerprintAuthenticated.get(userId));
             pw.println("    authSinceBoot="
                     + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
@@ -2063,5 +2359,18 @@
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
         }
+        if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+            final int userId = ActivityManager.getCurrentUser();
+            final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+            pw.println("  Face authentication state (user=" + userId + ")");
+            pw.println("    allowed=" + isUnlockingWithBiometricAllowed());
+            pw.println("    auth'd=" + mUserFaceAuthenticated.get(userId));
+            pw.println("    authSinceBoot="
+                    + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+            pw.println("    disabled(DPM)=" + isFaceDisabled(userId));
+            pw.println("    possible=" + isUnlockWithFacePossible(userId));
+            pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
+            pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 67571bb..8135ac5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -17,7 +17,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.graphics.Bitmap;
-import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.media.AudioManager;
 import android.os.SystemClock;
 import android.telephony.TelephonyManager;
@@ -213,38 +213,46 @@
     public void onTrustGrantedWithFlags(int flags, int userId) { }
 
     /**
-     * Called when a finger has been acquired.
+     * Called when a biometric has been acquired.
      * <p>
-     * It is guaranteed that either {@link #onFingerprintAuthenticated} or
-     * {@link #onFingerprintAuthFailed()} is called after this method eventually.
+     * It is guaranteed that either {@link #onBiometricAuthenticated} or
+     * {@link #onBiometricAuthFailed(BiometricSourceType)} is called after this method eventually.
+     * @param biometricSourceType
      */
-    public void onFingerprintAcquired() { }
+    public void onBiometricAcquired(BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when a fingerprint couldn't be authenticated.
+     * Called when a biometric couldn't be authenticated.
+     * @param biometricSourceType
      */
-    public void onFingerprintAuthFailed() { }
+    public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when a fingerprint is recognized.
-     * @param userId the user id for which the fingerprint was authenticated
+     * Called when a biometric is recognized.
+     * @param userId the user id for which the biometric sample was authenticated
+     * @param biometricSourceType
      */
-    public void onFingerprintAuthenticated(int userId) { }
+    public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when fingerprint provides help string (e.g. "Try again")
+     * Called when biometric authentication provides help string (e.g. "Try again")
      * @param msgId
      * @param helpString
+     * @param biometricSourceType
      */
-    public void onFingerprintHelp(int msgId, String helpString) { }
+    public void onBiometricHelp(int msgId, String helpString,
+            BiometricSourceType biometricSourceType) { }
 
     /**
-     * Called when fingerprint provides an semi-permanent error message
-     * (e.g. "Hardware not available").
-     * @param msgId one of the error messages listed in {@link FingerprintManager}
+     * Called when biometric authentication method provides a semi-permanent
+     * error message (e.g. "Hardware not available").
+     * @param msgId one of the error messages listed in
+     *        {@link android.hardware.biometrics.BiometricConstants}
      * @param errString
+     * @param biometricSourceType
      */
-    public void onFingerprintError(int msgId, String errString) { }
+    public void onBiometricError(int msgId, String errString,
+            BiometricSourceType biometricSourceType) { }
 
     /**
      * Called when the state of face unlock changed.
@@ -252,9 +260,10 @@
     public void onFaceUnlockStateChanged(boolean running, int userId) { }
 
     /**
-     * Called when the fingerprint running state changed.
+     * Called when biometric running state changed.
      */
-    public void onFingerprintRunningStateChanged(boolean running) { }
+    public void onBiometricRunningStateChanged(boolean running,
+            BiometricSourceType biometricSourceType) { }
 
     /**
      * Called when the state that the user hasn't used strong authentication since quite some time
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index cbb69ee..1e458fa 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -26,7 +27,7 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.internal.util.LatencyTracker;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
@@ -72,10 +73,10 @@
     }
 
     private void fakeWakeAndUnlock() {
-        FingerprintUnlockController fingerprintUnlockController = getComponent(StatusBar.class)
-                .getFingerprintUnlockController();
-        fingerprintUnlockController.onFingerprintAcquired();
-        fingerprintUnlockController.onFingerprintAuthenticated(
-                KeyguardUpdateMonitor.getCurrentUser());
+        BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class)
+                .getBiometricUnlockController();
+        biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+        biometricUnlockController.onBiometricAuthenticated(
+                KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index cf6f16a..b9d1021 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -30,6 +30,7 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -82,8 +83,8 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
@@ -522,18 +523,18 @@
         }
 
         @Override
-        public void onFingerprintAuthFailed() {
+        public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
             final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
             if (mLockPatternUtils.isSecure(currentUser)) {
-                mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+                mLockPatternUtils.getDevicePolicyManager().reportFailedBiometricAttempt(
                         currentUser);
             }
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId) {
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
             if (mLockPatternUtils.isSecure(userId)) {
-                mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+                mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt(
                         userId);
             }
         }
@@ -1645,7 +1646,7 @@
         }
 
         mUpdateMonitor.clearFailedUnlockAttempts();
-        mUpdateMonitor.clearFingerprintRecognized();
+        mUpdateMonitor.clearBiometricRecognized();
 
         if (mGoingToSleep) {
             Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
@@ -2051,9 +2052,9 @@
 
     public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
             ViewGroup container, NotificationPanelView panelView,
-            FingerprintUnlockController fingerprintUnlockController) {
+            BiometricUnlockController biometricUnlockController) {
         mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, panelView,
-                fingerprintUnlockController, mDismissCallbackRegistry);
+                biometricUnlockController, mDismissCallbackRegistry);
         return mStatusBarKeyguardViewManager;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 8a0d7e3..64ccba8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -60,6 +60,7 @@
 import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -246,7 +247,10 @@
 
         try {
             // Create screenshot directory if it doesn't exist
-            mScreenshotDir.mkdirs();
+            boolean madeDirs = mScreenshotDir.mkdirs();
+            if (madeDirs == false) {
+                Log.e(TAG, "Couldn't create screenshot directory: " + mScreenshotDir);
+            }
 
             // media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds
             // for DATE_TAKEN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 35e1511..294e2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -26,6 +27,7 @@
 import android.content.res.Resources;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -70,8 +72,8 @@
     private static final boolean DEBUG_CHARGING_SPEED = 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 static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
+    private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
 
     private final Context mContext;
     private ViewGroup mIndicationArea;
@@ -461,7 +463,7 @@
         public void handleMessage(Message msg) {
             if (msg.what == MSG_HIDE_TRANSIENT) {
                 hideTransientIndication();
-            } else if (msg.what == MSG_CLEAR_FP_MSG) {
+            } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
                 mLockIcon.setTransientFpError(false);
             }
         }
@@ -526,9 +528,10 @@
         }
 
         @Override
-        public void onFingerprintHelp(int msgId, String helpString) {
+        public void onBiometricHelp(int msgId, String helpString,
+                BiometricSourceType biometricSourceType) {
             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-            if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+            if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
                 return;
             }
             ColorStateList errorColorState = Utils.getColorError(mContext);
@@ -538,10 +541,10 @@
             } else if (updateMonitor.isScreenOn()) {
                 mLockIcon.setTransientFpError(true);
                 showTransientIndication(helpString, errorColorState);
-                hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
-                mHandler.removeMessages(MSG_CLEAR_FP_MSG);
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
-                        TRANSIENT_FP_ERROR_TIMEOUT);
+                hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+                mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
+                        TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             }
             // Help messages indicate that there was actually a try since the last error, so those
             // are not two successive error messages anymore.
@@ -549,16 +552,15 @@
         }
 
         @Override
-        public void onFingerprintError(int msgId, String errString) {
+        public void onBiometricError(int msgId, String errString,
+                BiometricSourceType biometricSourceType) {
             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-            if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
-                    && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
-                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+            if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
                 return;
             }
             ColorStateList errorColorState = Utils.getColorError(mContext);
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-                // When swiping up right after receiving a fingerprint error, the bouncer calls
+                // When swiping up right after receiving a biometric error, the bouncer calls
                 // authenticate leading to the same message being shown again on the bouncer.
                 // We want to avoid this, as it may confuse the user when the message is too
                 // generic.
@@ -576,6 +578,28 @@
             mLastSuccessiveErrorMessage = msgId;
         }
 
+        private boolean shouldSuppressBiometricError(int msgId,
+                BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
+            if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+                return shouldSuppressFingerprintError(msgId, updateMonitor);
+            if (biometricSourceType == BiometricSourceType.FACE)
+                return shouldSuppressFaceError(msgId, updateMonitor);
+            return false;
+        }
+
+        private boolean shouldSuppressFingerprintError(int msgId,
+                KeyguardUpdateMonitor updateMonitor) {
+            return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+                    && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+                    || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+
+        private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
+            return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+                    && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
+                    || msgId == FaceManager.FACE_ERROR_CANCELED);
+        }
+
         @Override
         public void onTrustAgentErrorMessage(CharSequence message) {
             showTransientIndication(message, Utils.getColorError(mContext));
@@ -592,21 +616,22 @@
         }
 
         @Override
-        public void onFingerprintRunningStateChanged(boolean running) {
+        public void onBiometricRunningStateChanged(boolean running,
+                BiometricSourceType biometricSourceType) {
             if (running) {
                 mMessageToShowOnScreenOn = null;
             }
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId) {
-            super.onFingerprintAuthenticated(userId);
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+            super.onBiometricAuthenticated(userId, biometricSourceType);
             mLastSuccessiveErrorMessage = -1;
         }
 
         @Override
-        public void onFingerprintAuthFailed() {
-            super.onFingerprintAuthFailed();
+        public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+            super.onBiometricAuthFailed(biometricSourceType);
             mLastSuccessiveErrorMessage = -1;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index ccbf483..6ee3189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -243,7 +243,7 @@
     }
 
     @Override
-    public void setVisibleState(int state) {
+    public void setVisibleState(int state, boolean animate) {
         if (state == mVisibleState) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 0ed6b77..513cd32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -124,7 +124,7 @@
     }
 
     @Override
-    public void setVisibleState(int state) {
+    public void setVisibleState(int state, boolean animate) {
         if (state == mVisibleState) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index b831b86..beb90b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -22,7 +22,10 @@
     String getSlot();
     void setStaticDrawableColor(int color);
     void setDecorColor(int color);
-    void setVisibleState(int state);
+    default void setVisibleState(int state) {
+        setVisibleState(state, false);
+    }
+    void setVisibleState(int state, boolean animate);
     int getVisibleState();
     boolean isIconVisible();
     default boolean isIconBlocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f0b1a82..0b6fd13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.Context;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -35,14 +36,14 @@
 import java.io.PrintWriter;
 
 /**
- * Controller which coordinates all the fingerprint unlocking actions with the UI.
+ * Controller which coordinates all the biometric unlocking actions with the UI.
  */
-public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
+public class BiometricUnlockController 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";
+    private static final String TAG = "BiometricUnlockController";
+    private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK;
+    private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
+    private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
 
     /**
      * Mode in which we don't need to wake up the device when we get a fingerprint.
@@ -90,9 +91,9 @@
     public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7;
 
     /**
-     * How much faster we collapse the lockscreen when authenticating with fingerprint.
+     * How much faster we collapse the lockscreen when authenticating with biometric.
      */
-    private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
+    private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
 
     private PowerManager mPowerManager;
     private Handler mHandler = new Handler();
@@ -108,15 +109,16 @@
     private final UnlockMethodCache mUnlockMethodCache;
     private final Context mContext;
     private int mPendingAuthenticatedUserId = -1;
+    private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
     private boolean mPendingShowBouncer;
     private boolean mHasScreenTurnedOnSinceAuthenticating;
 
-    public FingerprintUnlockController(Context context,
-            DozeScrimController dozeScrimController,
-            KeyguardViewMediator keyguardViewMediator,
-            ScrimController scrimController,
-            StatusBar statusBar,
-            UnlockMethodCache unlockMethodCache) {
+    public BiometricUnlockController(Context context,
+                                     DozeScrimController dozeScrimController,
+                                     KeyguardViewMediator keyguardViewMediator,
+                                     ScrimController scrimController,
+                                     StatusBar statusBar,
+                                     UnlockMethodCache unlockMethodCache) {
         mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
@@ -136,21 +138,21 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
     }
 
-    private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
+    private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
         @Override
         public void run() {
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fp wakelock: TIMEOUT!!");
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "biometric wakelock: TIMEOUT!!");
             }
-            releaseFingerprintWakeLock();
+            releaseBiometricWakeLock();
         }
     };
 
-    private void releaseFingerprintWakeLock() {
+    private void releaseBiometricWakeLock() {
         if (mWakeLock != null) {
-            mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "releasing fp wakelock");
+            mHandler.removeCallbacks(mReleaseBiometricWakeLockRunnable);
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "releasing biometric wakelock");
             }
             mWakeLock.release();
             mWakeLock = null;
@@ -158,24 +160,27 @@
     }
 
     @Override
-    public void onFingerprintAcquired() {
-        Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
-        releaseFingerprintWakeLock();
+    public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
+        Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
+        releaseBiometricWakeLock();
         if (!mUpdateMonitor.isDeviceInteractive()) {
             if (LatencyTracker.isEnabled(mContext)) {
-                LatencyTracker.getInstance(mContext).onActionStart(
-                        LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
+                int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+                if (biometricSourceType == BiometricSourceType.FACE) {
+                    action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
+                }
+                LatencyTracker.getInstance(mContext).onActionStart(action);
             }
             mWakeLock = mPowerManager.newWakeLock(
-                    PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
+                    PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME);
             Trace.beginSection("acquiring wake-and-unlock");
             mWakeLock.acquire();
             Trace.endSection();
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "biometric acquired, grabbing biometric wakelock");
             }
-            mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
-                    FINGERPRINT_WAKELOCK_TIMEOUT_MS);
+            mHandler.postDelayed(mReleaseBiometricWakeLockRunnable,
+                    BIOMETRIC_WAKELOCK_TIMEOUT_MS);
         }
         Trace.endSection();
     }
@@ -187,10 +192,11 @@
     }
 
     @Override
-    public void onFingerprintAuthenticated(int userId) {
-        Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
+    public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+        Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
         if (mUpdateMonitor.isGoingToSleep()) {
             mPendingAuthenticatedUserId = userId;
+            mPendingAuthenticatedBioSourceType = biometricSourceType;
             Trace.endSection();
             return;
         }
@@ -211,13 +217,13 @@
             mStatusBarWindowManager.setForceDozeBrightness(true);
         }
         if (!wasDeviceInteractive) {
-            if (DEBUG_FP_WAKELOCK) {
-                Log.i(TAG, "fp wakelock: Authenticated, waking up...");
+            if (DEBUG_BIO_WAKELOCK) {
+                Log.i(TAG, "bio wakelock: Authenticated, waking up...");
             }
-            mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
+            mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:BIOMETRIC");
         }
         Trace.beginSection("release wake-and-unlock");
-        releaseFingerprintWakeLock();
+        releaseBiometricWakeLock();
         Trace.endSection();
         switch (mMode) {
             case MODE_DISMISS_BOUNCER:
@@ -261,7 +267,7 @@
             case MODE_NONE:
                 break;
         }
-        mStatusBar.notifyFpAuthModeChanged();
+        mStatusBar.notifyBiometricAuthModeChanged();
         Trace.endSection();
     }
 
@@ -270,7 +276,7 @@
             mStatusBarKeyguardViewManager.showBouncer(false);
         }
         mStatusBarKeyguardViewManager.animateCollapsePanels(
-                FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
+                BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
         mPendingShowBouncer = false;
     }
 
@@ -278,28 +284,31 @@
     public void onStartedGoingToSleep(int why) {
         resetMode();
         mPendingAuthenticatedUserId = -1;
+        mPendingAuthenticatedBioSourceType = null;
     }
 
     @Override
     public void onFinishedGoingToSleep(int why) {
-        Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
+        Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
         if (mPendingAuthenticatedUserId != -1) {
 
             // Post this to make sure it's executed after the device is fully locked.
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    onFingerprintAuthenticated(mPendingAuthenticatedUserId);
+                    onBiometricAuthenticated(mPendingAuthenticatedUserId,
+                            mPendingAuthenticatedBioSourceType);
                 }
             });
         }
         mPendingAuthenticatedUserId = -1;
+        mPendingAuthenticatedBioSourceType = null;
         Trace.endSection();
     }
 
     public boolean hasPendingAuthentication() {
         return mPendingAuthenticatedUserId != -1
-                && mUpdateMonitor.isUnlockingWithFingerprintAllowed()
+                && mUpdateMonitor.isUnlockingWithBiometricAllowed()
                 && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
     }
 
@@ -308,7 +317,7 @@
     }
 
     private int calculateMode() {
-        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
+        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
         boolean deviceDreaming = mUpdateMonitor.isDreaming();
 
         if (!mUpdateMonitor.isDeviceInteractive()) {
@@ -338,17 +347,18 @@
     }
 
     @Override
-    public void onFingerprintAuthFailed() {
+    public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
         cleanup();
     }
 
     @Override
-    public void onFingerprintError(int msgId, String errString) {
+    public void onBiometricError(int msgId, String errString,
+            BiometricSourceType biometricSourceType) {
         cleanup();
     }
 
     private void cleanup() {
-        releaseFingerprintWakeLock();
+        releaseBiometricWakeLock();
     }
 
     public void startKeyguardFadingAway() {
@@ -372,7 +382,7 @@
         if (mStatusBar.getNavigationBarView() != null) {
             mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
         }
-        mStatusBar.notifyFpAuthModeChanged();
+        mStatusBar.notifyBiometricAuthModeChanged();
     }
 
     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
@@ -380,7 +390,7 @@
         @Override
         public void onFinishedWakingUp() {
             if (mPendingShowBouncer) {
-                FingerprintUnlockController.this.showBouncer();
+                BiometricUnlockController.this.showBouncer();
             }
         }
     };
@@ -398,7 +408,7 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println(" FingerprintUnlockController:");
+        pw.println(" BiometricUnlockController:");
         pw.print("   mMode="); pw.println(mMode);
         pw.print("   mWakeLock="); pw.println(mWakeLock);
     }
@@ -415,7 +425,7 @@
     /**
      * Successful authentication with fingerprint when the screen was either on or off.
      */
-    public boolean isFingerprintUnlock() {
+    public boolean isBiometricUnlock() {
         return isWakeAndUnlock() || mMode == MODE_UNLOCK;
     }
 }
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 3b12051..25b97bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -28,6 +28,7 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -774,7 +775,8 @@
                 }
 
                 @Override
-                public void onFingerprintRunningStateChanged(boolean running) {
+                public void onBiometricRunningStateChanged(boolean running,
+                        BiometricSourceType biometricSourceType) {
                     mLockIcon.update();
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 8b8cbfe..8cace72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -44,7 +44,7 @@
 
     private final DarkIconDispatcher mStatusBarIconController;
     private final BatteryController mBatteryController;
-    private FingerprintUnlockController mFingerprintUnlockController;
+    private BiometricUnlockController mBiometricUnlockController;
 
     private LightBarTransitionsController mNavigationBarController;
     private int mSystemUiVisibility;
@@ -89,9 +89,9 @@
         updateNavigation();
     }
 
-    public void setFingerprintUnlockController(
-            FingerprintUnlockController fingerprintUnlockController) {
-        mFingerprintUnlockController = fingerprintUnlockController;
+    public void setBiometricUnlockController(
+            BiometricUnlockController biometricUnlockController) {
+        mBiometricUnlockController = biometricUnlockController;
     }
 
     public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
@@ -178,12 +178,12 @@
     }
 
     private boolean animateChange() {
-        if (mFingerprintUnlockController == null) {
+        if (mBiometricUnlockController == null) {
             return false;
         }
-        int unlockMode = mFingerprintUnlockController.getMode();
-        return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+        int unlockMode = mBiometricUnlockController.getMode();
+        return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
     }
 
     private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
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 4b66ee5a..8928530 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -306,7 +306,7 @@
     private int getState() {
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
         boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
-        boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
+        boolean unlockingAllowed = updateMonitor.isUnlockingWithBiometricAllowed();
         if (mTransientFpError) {
             return STATE_FINGERPRINT_ERROR;
         } else if (mUnlockMethodCache.canSkipBouncer()) {
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 4a98262..6077e79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -18,6 +18,7 @@
 
 import static android.view.MotionEvent.ACTION_DOWN;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 
@@ -336,15 +337,15 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (shouldDeadZoneConsumeTouchEvents(event)) {
-            return true;
-        }
+        final boolean deadZoneConsumed = shouldDeadZoneConsumeTouchEvents(event);
         switch (event.getActionMasked()) {
             case ACTION_DOWN:
                 int x = (int) event.getX();
                 int y = (int) event.getY();
                 mDownHitTarget = HIT_TARGET_NONE;
-                if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
+                if (deadZoneConsumed) {
+                    mDownHitTarget = HIT_TARGET_DEAD_ZONE;
+                } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
                     mDownHitTarget = HIT_TARGET_BACK;
                 } else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) {
                     mDownHitTarget = HIT_TARGET_HOME;
@@ -361,9 +362,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (shouldDeadZoneConsumeTouchEvents(event)) {
-            return true;
-        }
+        shouldDeadZoneConsumeTouchEvents(event);
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
@@ -772,7 +771,7 @@
                 showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);
     }
 
-    private void updateSlippery() {
+    public void updateSlippery() {
         setSlippery(!isQuickStepSwipeUpEnabled() || mPanelView.isFullyExpanded());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 18bc4e5..5d23494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -33,6 +33,7 @@
     private static final String STATE = "state";
     private boolean mBouncerShowing;
     private boolean mExpanded;
+    protected float mPanelFraction;
 
     public static final void LOG(String fmt, Object... args) {
         if (!DEBUG) return;
@@ -99,6 +100,14 @@
         if (mPanel != null) mPanel.setImportantForAccessibility(important);
     }
 
+    public float getExpansionFraction() {
+        return mPanelFraction;
+    }
+
+    public boolean isExpanded() {
+        return mExpanded;
+    }
+
     private void updateVisibility() {
         mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
     }
@@ -153,6 +162,7 @@
         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
         PanelView pv = mPanel;
         mExpanded = expanded;
+        mPanelFraction = frac;
         updateVisibility();
         // adjust any other panels that may be partially visible
         if (expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 075883a..59863ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -59,7 +59,6 @@
     private final PhoneStatusBarTransitions mBarTransitions;
     private ScrimController mScrimController;
     private float mMinFraction;
-    private float mPanelFraction;
     private Runnable mHideExpandedRunnable = new Runnable() {
         @Override
         public void run() {
@@ -269,7 +268,6 @@
     @Override
     public void panelExpansionChanged(float frac, boolean expanded) {
         super.panelExpansionChanged(frac, expanded);
-        mPanelFraction = frac;
         updateScrimFraction();
         if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
             mBar.getNavigationBarView().onPanelExpandedChange(expanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 15790d4..765cc49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.Interpolators.ALPHA_OUT;
 import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
 import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -185,6 +186,8 @@
     }
 
     private boolean handleTouchEvent(MotionEvent event) {
+        final boolean deadZoneConsumed =
+                mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
         if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
                 && !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
             return false;
@@ -304,7 +307,7 @@
                 || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)) {
             proxyMotionEvents(event);
         }
-        return mQuickScrubActive || mQuickStepStarted;
+        return mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
     }
 
     @Override
@@ -438,6 +441,9 @@
             mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
             mTrackAnimator.start();
 
+            // Disable slippery for quick scrub to not cancel outside the nav bar
+            mNavigationBarView.updateSlippery();
+
             try {
                 mOverviewEventSender.getProxy().onQuickScrubStart();
                 if (DEBUG_OVERVIEW_PROXY) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c2f31bf..d8e3dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -361,7 +361,7 @@
     private VolumeComponent mVolumeComponent;
     private BrightnessMirrorController mBrightnessMirrorController;
     private boolean mBrightnessMirrorVisible;
-    protected FingerprintUnlockController mFingerprintUnlockController;
+    protected BiometricUnlockController mBiometricUnlockController;
     private LightBarController mLightBarController;
     protected LockscreenWallpaper mLockscreenWallpaper;
 
@@ -505,7 +505,7 @@
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
 
-    private BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -513,7 +513,7 @@
                 Log.w(TAG, "WallpaperManager not available");
                 return;
             }
-            WallpaperInfo info = wallpaperManager.getWallpaperInfo();
+            WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
             final boolean supportsAmbientMode = info != null &&
                     info.getSupportsAmbientMode();
 
@@ -707,7 +707,8 @@
 
         // Make sure we always have the most current wallpaper info.
         IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
-        mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
+        mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
+                wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
         mWallpaperChangedReceiver.onReceive(mContext, null);
 
         mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
@@ -832,6 +833,7 @@
                     CollapsedStatusBarFragment statusBarFragment =
                             (CollapsedStatusBarFragment) fragment;
                     statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
+                    PhoneStatusBarView oldStatusBarView = mStatusBarView;
                     mStatusBarView = (PhoneStatusBarView) fragment.getView();
                     mStatusBarView.setBar(this);
                     mStatusBarView.setPanel(mNotificationPanel);
@@ -848,6 +850,11 @@
                         mNotificationPanel.notifyBarPanelExpansionChanged();
                     }
                     mStatusBarView.setBouncerShowing(mBouncerShowing);
+                    if (oldStatusBarView != null) {
+                        float fraction = oldStatusBarView.getExpansionFraction();
+                        boolean expanded = oldStatusBarView.isExpanded();
+                        mStatusBarView.panelExpansionChanged(fraction, expanded);
+                    }
 
                     HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
                     if (mHeadsUpAppearanceController != null) {
@@ -1322,18 +1329,18 @@
     protected void startKeyguard() {
         Trace.beginSection("StatusBar#startKeyguard");
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
-        mFingerprintUnlockController = new FingerprintUnlockController(mContext,
+        mBiometricUnlockController = new BiometricUnlockController(mContext,
                 mDozeScrimController, keyguardViewMediator,
                 mScrimController, this, UnlockMethodCache.getInstance(mContext));
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
-                getBouncerContainer(), mNotificationPanel, mFingerprintUnlockController);
+                getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
-        mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+        mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
-        mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+        mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
         Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked);
         Trace.endSection();
     }
@@ -1617,8 +1624,8 @@
             return; // called too early
         }
 
-        boolean wakeAndUnlock = mFingerprintUnlockController != null
-            && mFingerprintUnlockController.isWakeAndUnlock();
+        boolean wakeAndUnlock = mBiometricUnlockController != null
+            && mBiometricUnlockController.isWakeAndUnlock();
         if (mLaunchTransitionFadingAway || wakeAndUnlock) {
             mBackdrop.setVisibility(View.INVISIBLE);
             Trace.endSection();
@@ -1670,8 +1677,8 @@
 
         if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
                 && (mState != StatusBarState.SHADE || allowWhenShade)
-                && mFingerprintUnlockController.getMode()
-                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && mBiometricUnlockController.getMode()
+                        != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
                 && !hideBecauseOccluded) {
             // time to show some art!
             if (mBackdrop.getVisibility() != View.VISIBLE) {
@@ -1736,8 +1743,8 @@
                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
                 }
                 boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
-                if (mFingerprintUnlockController.getMode()
-                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                if (mBiometricUnlockController.getMode()
+                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
                         || hideBecauseOccluded || cannotAnimateDoze) {
 
                     // We are unlocking directly - no animation!
@@ -2469,8 +2476,8 @@
         return mGestureRec;
     }
 
-    public FingerprintUnlockController getFingerprintUnlockController() {
-        return mFingerprintUnlockController;
+    public BiometricUnlockController getBiometricUnlockController() {
+        return mBiometricUnlockController;
     }
 
     @Override // CommandQueue
@@ -2804,8 +2811,8 @@
 
         DozeLog.dump(pw);
 
-        if (mFingerprintUnlockController != null) {
-            mFingerprintUnlockController.dump(pw);
+        if (mBiometricUnlockController != null) {
+            mBiometricUnlockController.dump(pw);
         }
 
         if (mKeyguardIndicationController != null) {
@@ -3113,10 +3120,10 @@
                 && mUnlockMethodCache.canSkipBouncer()
                 && !mLeaveOpenOnKeyguardHide
                 && isPulsing()) {
-            // Reuse the fingerprint wake-and-unlock transition if we dismiss keyguard from a pulse.
-            // TODO: Factor this transition out of FingerprintUnlockController.
-            mFingerprintUnlockController.startWakeAndUnlock(
-                    FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
+            // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
+            // TODO: Factor this transition out of BiometricUnlockController.
+            mBiometricUnlockController.startWakeAndUnlock(
+                    BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
         }
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
@@ -3156,6 +3163,7 @@
         updateNotificationViews();
         mMediaManager.clearCurrentMediaNotification();
         setLockscreenUser(newUserId);
+        mWallpaperChangedReceiver.onReceive(mContext, null);
     }
 
     @Override
@@ -3520,8 +3528,8 @@
     }
 
     private boolean updateIsKeyguard() {
-        boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
-                == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+        boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
+                == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 
         // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
         // there's no surface we can show to the user. Note that the device goes fully interactive
@@ -3568,8 +3576,8 @@
     }
 
     private void updatePanelExpansionForKeyguard() {
-        if (mState == StatusBarState.KEYGUARD && mFingerprintUnlockController.getMode()
-                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+        if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
+                != BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
             instantExpandNotificationsPanel();
         } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
             instantCollapseNotificationPanel();
@@ -4697,7 +4705,7 @@
                 || mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
     }
 
-    public void notifyFpAuthModeChanged() {
+    public void notifyBiometricAuthModeChanged() {
         updateDozing();
         updateScrimController();
     }
@@ -4706,13 +4714,13 @@
         Trace.beginSection("StatusBar#updateDozing");
         // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
         boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
-                || mFingerprintUnlockController.getMode()
-                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+                || mBiometricUnlockController.getMode()
+                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
         final boolean alwaysOn = DozeParameters.getInstance(mContext).getAlwaysOn();
         // When in wake-and-unlock we may not have received a change to mState
         // but we still should not be dozing, manually set to false.
-        if (mFingerprintUnlockController.getMode() ==
-                FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+        if (mBiometricUnlockController.getMode() ==
+                mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
             dozing = false;
         }
         if (mDozing != dozing) {
@@ -4736,11 +4744,11 @@
 
         // We don't want to end up in KEYGUARD state when we're unlocking with
         // fingerprint from doze. We should cross fade directly from black.
-        boolean wakeAndUnlocking = mFingerprintUnlockController.isWakeAndUnlock();
+        boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock();
 
         // Do not animate the scrim expansion when triggered by the fingerprint sensor.
         mScrimController.setExpansionAffectsAlpha(
-                !mFingerprintUnlockController.isFingerprintUnlock());
+                !mBiometricUnlockController.isBiometricUnlock());
 
         if (mBouncerShowing) {
             // Bouncer needs the front scrim when it's on top of an activity,
@@ -4882,8 +4890,8 @@
 
         @Override
         public boolean isPulsingBlocked() {
-            return mFingerprintUnlockController.getMode()
-                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+            return mBiometricUnlockController.getMode()
+                    == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
         }
 
         @Override
@@ -4894,7 +4902,7 @@
 
         @Override
         public boolean isBlockingDoze() {
-            if (mFingerprintUnlockController.hasPendingAuthentication()) {
+            if (mBiometricUnlockController.hasPendingAuthentication()) {
                 Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
                 return true;
             }
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 affc424..378910a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
 
 import android.content.ComponentCallbacks2;
 import android.content.Context;
@@ -91,7 +91,7 @@
     protected ViewMediatorCallback mViewMediatorCallback;
     protected StatusBar mStatusBar;
     private NotificationPanelView mNotificationPanelView;
-    private FingerprintUnlockController mFingerprintUnlockController;
+    private BiometricUnlockController mBiometricUnlockController;
 
     private ViewGroup mContainer;
 
@@ -108,7 +108,7 @@
     private boolean mLastBouncerDismissible;
     protected boolean mLastRemoteInputActive;
     private boolean mLastDozing;
-    private int mLastFpMode;
+    private int mLastBiometricMode;
     private boolean mGoingToSleepVisibleNotOccluded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
@@ -142,11 +142,11 @@
     public void registerStatusBar(StatusBar statusBar,
             ViewGroup container,
             NotificationPanelView notificationPanelView,
-            FingerprintUnlockController fingerprintUnlockController,
+            BiometricUnlockController biometricUnlockController,
             DismissCallbackRegistry dismissCallbackRegistry) {
         mStatusBar = statusBar;
         mContainer = container;
-        mFingerprintUnlockController = fingerprintUnlockController;
+        mBiometricUnlockController = biometricUnlockController;
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
                 mExpansionCallback);
@@ -259,7 +259,7 @@
     }
 
     private boolean isWakeAndUnlocking() {
-        int mode = mFingerprintUnlockController.getMode();
+        int mode = mBiometricUnlockController.getMode();
         return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING;
     }
 
@@ -442,13 +442,13 @@
         } else {
             executeAfterKeyguardGoneAction();
             boolean wakeUnlockPulsing =
-                    mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
+                    mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
             if (wakeUnlockPulsing) {
                 delay = 0;
                 fadeoutDuration = 240;
             }
             mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
-            mFingerprintUnlockController.startKeyguardFadingAway();
+            mBiometricUnlockController.startKeyguardFadingAway();
             hideBouncer(true /* destroyView */);
             if (wakeUnlockPulsing) {
                 mStatusBar.fadeKeyguardWhilePulsing();
@@ -460,7 +460,7 @@
                     wakeAndUnlockDejank();
                 } else {
                     mStatusBar.finishKeyguardFadingAway();
-                    mFingerprintUnlockController.finishKeyguardFadingAway();
+                    mBiometricUnlockController.finishKeyguardFadingAway();
                 }
             }
             updateStates();
@@ -484,14 +484,14 @@
         mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
                 100);
         mStatusBar.finishKeyguardFadingAway();
-        mFingerprintUnlockController.finishKeyguardFadingAway();
+        mBiometricUnlockController.finishKeyguardFadingAway();
         WindowManagerGlobal.getInstance().trimMemory(
                 ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
 
     }
 
     private void wakeAndUnlockDejank() {
-        if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
+        if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
                 && LatencyTracker.isEnabled(mContext)) {
             DejankUtils.postAfterTraversal(() ->
                     LatencyTracker.getInstance(mContext).onActionEnd(
@@ -618,7 +618,7 @@
         mLastBouncerDismissible = bouncerDismissible;
         mLastRemoteInputActive = remoteInputActive;
         mLastDozing = mDozing;
-        mLastFpMode = mFingerprintUnlockController.getMode();
+        mLastBiometricMode = mBiometricUnlockController.getMode();
         mStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
@@ -643,9 +643,9 @@
      * @return Whether the navigation bar should be made visible based on the current state.
      */
     protected boolean isNavBarVisible() {
-        int fpMode = mFingerprintUnlockController.getMode();
+        int biometricMode = mBiometricUnlockController.getMode();
         boolean keyguardShowing = mShowing && !mOccluded;
-        boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
         return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
                 || mRemoteInputActive);
     }
@@ -655,7 +655,7 @@
      */
     protected boolean getLastNavBarVisible() {
         boolean keyguardShowing = mLastShowing && !mLastOccluded;
-        boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
         return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
                 || mLastRemoteInputActive);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 0811179..f8f6981 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -301,11 +301,7 @@
 
             vs.initFrom(child);
             vs.alpha = 1.0f;
-            if (child instanceof StatusIconDisplayable) {
-                vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
-            } else {
-                vs.hidden = false;
-            }
+            vs.hidden = false;
         }
     }
 
@@ -333,22 +329,33 @@
             }
             StatusIconDisplayable icon = (StatusIconDisplayable) view;
             AnimationProperties animationProperties = null;
-            boolean animate = false;
+            boolean animateVisibility = true;
 
-            if (justAdded) {
+            // Figure out which properties of the state transition (if any) we need to animate
+            if (justAdded
+                    || icon.getVisibleState() == STATE_HIDDEN && visibleState == STATE_ICON) {
+                // Icon is appearing, fade it in by putting it where it will be and animating alpha
                 super.applyToView(view);
+                view.setAlpha(0.f);
+                icon.setVisibleState(STATE_HIDDEN);
                 animationProperties = ADD_ICON_PROPERTIES;
-                animate = true;
             } else if (icon.getVisibleState() != visibleState) {
-                animationProperties = DOT_ANIMATION_PROPERTIES;
-                animate = true;
+                if (icon.getVisibleState() == STATE_ICON && visibleState == STATE_HIDDEN) {
+                    // Disappearing, don't do anything fancy
+                    animateVisibility = false;
+                } else {
+                    // all other transitions (to/from dot, etc)
+                    animationProperties = ANIMATE_ALL_PROPERTIES;
+                }
+            } else if (visibleState != STATE_HIDDEN && xTranslation != view.getTranslationX()) {
+                // Visibility isn't changing, just animate position
+                animationProperties = X_ANIMATION_PROPERTIES;
             }
 
-            if (animate) {
+            icon.setVisibleState(visibleState, animateVisibility);
+            if (animationProperties != null) {
                 animateTo(view, animationProperties);
-                icon.setVisibleState(visibleState);
             } else {
-                icon.setVisibleState(visibleState);
                 super.applyToView(view);
             }
 
@@ -365,7 +372,7 @@
         }
     }.setDuration(200).setDelay(50);
 
-    private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
+    private static final AnimationProperties X_ANIMATION_PROPERTIES = new AnimationProperties() {
         private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
 
         @Override
@@ -373,4 +380,14 @@
             return mAnimationFilter;
         }
     }.setDuration(200);
+
+    private static final AnimationProperties ANIMATE_ALL_PROPERTIES = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateX().animateY()
+                .animateAlpha().animateScale();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(200);
 }
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 f9c2130..e5925f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.hardware.biometrics.BiometricSourceType;
 import android.content.Context;
 import android.os.Trace;
 
@@ -135,9 +136,9 @@
         }
 
         @Override
-        public void onFingerprintAuthenticated(int userId) {
-            Trace.beginSection("KeyguardUpdateMonitorCallback#onFingerprintAuthenticated");
-            if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+            Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
+            if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
                 Trace.endSection();
                 return;
             }
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 312119b..4264adad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -983,7 +983,7 @@
      *         Measured relative to the resting position.
      */
     private float getExpandTranslationStart() {
-        return - mTopPadding;
+        return -mTopPadding + getMinExpansionHeight();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 67fa049..c468fef 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -113,7 +113,7 @@
         NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP,
                 name, NotificationManager.IMPORTANCE_HIGH); // pop on screen
 
-        screenshotChannel.setSound(Uri.parse(""), // silent
+        screenshotChannel.setSound(null, // silent
                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
         screenshotChannel.setBlockableSystem(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 94ab9d2..6933328 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -60,7 +60,7 @@
     @Mock
     private NotificationPanelView mNotificationPanelView;
     @Mock
-    private FingerprintUnlockController mFingerprintUnlockController;
+    private BiometricUnlockController mBiometrucUnlockController;
     @Mock
     private DismissCallbackRegistry mDismissCallbackRegistry;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -72,7 +72,7 @@
         mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(getContext(),
                 mViewMediatorCallback, mLockPatternUtils);
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
-                mNotificationPanelView, mFingerprintUnlockController, mDismissCallbackRegistry);
+                mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry);
         mStatusBarKeyguardViewManager.show(null);
     }
 
@@ -170,8 +170,8 @@
 
     @Test
     public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
-        when(mFingerprintUnlockController.getMode())
-                .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+        when(mBiometrucUnlockController.getMode())
+                .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
         mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
                 false /* tracking */);
         verify(mBouncer, never()).setExpansion(anyFloat());
@@ -196,7 +196,7 @@
         @Override
         public void registerStatusBar(StatusBar statusBar, ViewGroup container,
                 NotificationPanelView notificationPanelView,
-                FingerprintUnlockController fingerprintUnlockController,
+                BiometricUnlockController fingerprintUnlockController,
                 DismissCallbackRegistry dismissCallbackRegistry) {
             super.registerStatusBar(statusBar, container, notificationPanelView,
                     fingerprintUnlockController, dismissCallbackRegistry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cf23bfc..f908dfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -132,7 +132,7 @@
     @Mock private IStatusBarService mBarService;
     @Mock private ScrimController mScrimController;
     @Mock private ArrayList<Entry> mNotificationList;
-    @Mock private FingerprintUnlockController mFingerprintUnlockController;
+    @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
 
     // Mock dependencies:
@@ -207,7 +207,7 @@
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
                 mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
-                mEntryManager, mScrimController, mFingerprintUnlockController,
+                mEntryManager, mScrimController, mBiometricUnlockController,
                 mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
                 mRemoteInputManager, mock(NotificationGroupManager.class),
                 mock(FalsingManager.class), mock(StatusBarWindowManager.class),
@@ -572,15 +572,15 @@
 
     @Test
     public void testFingerprintNotification_UpdatesScrims() {
-        mStatusBar.notifyFpAuthModeChanged();
+        mStatusBar.notifyBiometricAuthModeChanged();
         verify(mScrimController).transitionTo(any(), any());
     }
 
     @Test
     public void testFingerprintUnlock_UpdatesScrims() {
         // Simulate unlocking from AoD with fingerprint.
-        when(mFingerprintUnlockController.getMode())
-                .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+        when(mBiometricUnlockController.getMode())
+                .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
         mStatusBar.updateScrimController();
         verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
     }
@@ -717,7 +717,7 @@
                 VisualStabilityManager visualStabilityManager,
                 NotificationViewHierarchyManager viewHierarchyManager,
                 TestableNotificationEntryManager entryManager, ScrimController scrimController,
-                FingerprintUnlockController fingerprintUnlockController,
+                BiometricUnlockController biometricUnlockController,
                 ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
                 NotificationRemoteInputManager notificationRemoteInputManager,
                 NotificationGroupManager notificationGroupManager,
@@ -743,7 +743,7 @@
             mViewHierarchyManager = viewHierarchyManager;
             mEntryManager = entryManager;
             mScrimController = scrimController;
-            mFingerprintUnlockController = fingerprintUnlockController;
+            mBiometricUnlockController = biometricUnlockController;
             mActivityLaunchAnimator = launchAnimator;
             mKeyguardViewMediator = keyguardViewMediator;
             mClearAllEnabled = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 50b4f3f..0d398be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -103,7 +103,7 @@
                 NotificationManager.IMPORTANCE_MIN);
         NotificationChannel newChannel =
                 NotificationChannels.createScreenshotChannel("newName", legacyChannel);
-        assertEquals(Uri.EMPTY, newChannel.getSound());
+        assertEquals(null, newChannel.getSound());
         assertEquals("newName", newChannel.getName());
         // MIN importance not user locked, so HIGH wins out.
         assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
@@ -113,7 +113,7 @@
     public void testInheritFromLegacy_noLegacyExists() {
         NotificationChannel newChannel =
                 NotificationChannels.createScreenshotChannel("newName", null);
-        assertEquals(Uri.EMPTY, newChannel.getSound());
+        assertEquals(null, newChannel.getSound());
         assertEquals("newName", newChannel.getName());
         assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
     }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index a9a14ca..fba639c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -212,6 +212,10 @@
     // Package: android
     NOTE_AUTO_SAVER_SUGGESTION = 49;
 
+    // Notify the user that their softap config preference has changed.
+    // Package: android
+    NOTE_SOFTAP_CONFIG_CHANGED = 50;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 20875a9..4b8ece9 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -468,6 +468,13 @@
     IBinder mCurFocusedWindow;
 
     /**
+     * The last window token that we confirmed that IME started talking to.  This is always updated
+     * upon reports from the input method.  If the window state is already changed before the report
+     * is handled, this field just keeps the last value.
+     */
+    IBinder mLastImeTargetWindow;
+
+    /**
      * {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
      *
      * @see #mCurFocusedWindow
@@ -688,7 +695,7 @@
     }
 
     @GuardedBy("mMethodMap")
-    private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
+    private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
 
     /**
      * A ring buffer to store the history of {@link StartInputInfo}.
@@ -1809,7 +1816,7 @@
         final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
                 !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
                 mCurSeq);
-        mStartInputMap.put(startInputToken, info);
+        mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
         mStartInputHistory.addEntry(info);
 
         final SessionState session = mCurClient.curSession;
@@ -2219,15 +2226,12 @@
     @BinderThread
     @SuppressWarnings("deprecation")
     @Override
-    public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
-            int backDisposition) {
+    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
         if (!calledWithValidToken(token)) {
             return;
         }
 
-        final StartInputInfo info;
         synchronized (mMethodMap) {
-            info = mStartInputMap.get(startInputToken);
             mImeWindowVis = vis;
             mBackDisposition = backDisposition;
             updateSystemUiLocked(token, vis, backDisposition);
@@ -2247,8 +2251,7 @@
                 break;
         }
         mWindowManagerInternal.updateInputMethodWindowStatus(token,
-                (vis & InputMethodService.IME_VISIBLE) != 0,
-                dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
+                (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
     }
 
     private void updateSystemUi(IBinder token, int vis, int backDisposition) {
@@ -2257,6 +2260,22 @@
         }
     }
 
+    @BinderThread
+    @Override
+    public void reportStartInput(IBinder token, IBinder startInputToken) {
+        if (!calledWithValidToken(token)) {
+            return;
+        }
+
+        synchronized (mMethodMap) {
+            final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
+            if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+                mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
+            }
+            mLastImeTargetWindow = targetWindow;
+        }
+    }
+
     // Caution! This method is called in this class. Handle multi-user carefully
     private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
         if (!calledWithValidToken(token)) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 85dc11a..f49943e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1638,6 +1638,8 @@
      * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
      *                                  {@code true} if config changed.
      * @param deferResume Whether to defer resume while updating config.
+     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+     *         because of configuration update.
      */
     boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
             boolean markFrozenIfConfigChanged, boolean deferResume) {
@@ -1648,6 +1650,11 @@
         ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                 false /* preserveWindows */, false /* notifyClients */);
 
+        if (displayId == INVALID_DISPLAY) {
+            // The caller didn't provide a valid display id, skip updating config.
+            return true;
+        }
+
         // Force-update the orientation from the WindowManager, since we need the true configuration
         // to send to the client now.
         final Configuration config = mWindowManager.updateOrientationFromAppTokens(
diff --git a/services/core/java/com/android/server/biometrics/face/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/face/AuthenticationClient.java
new file mode 100644
index 0000000..f9e8793
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/AuthenticationClient.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import android.content.Context;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the authentication state for a given client.
+ */
+public abstract class AuthenticationClient extends ClientMonitor {
+    private long mOpId;
+
+    public abstract int handleFailedAttempt();
+    public abstract void resetFailedAttempts();
+
+    public static final int LOCKOUT_NONE = 0;
+    public static final int LOCKOUT_TIMED = 1;
+    public static final int LOCKOUT_PERMANENT = 2;
+
+    public AuthenticationClient(Context context, long halDeviceId, IBinder token,
+            IFaceServiceReceiver receiver, int targetUserId, long opId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, targetUserId, restricted, owner);
+        mOpId = opId;
+    }
+
+    @Override
+    public boolean onAuthenticated(int faceId) {
+        boolean result = false;
+        boolean authenticated = faceId != 0;
+
+        IFaceServiceReceiver receiver = getReceiver();
+        if (receiver != null) {
+            try {
+                MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
+                        authenticated); // TODO: Define ACTION_FACE_AUTH constant and use here
+                if (!authenticated) {
+                    receiver.onAuthenticationFailed(getHalDeviceId());
+                } else {
+                    if (DEBUG) {
+                        Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString());
+                    }
+                    Face face = !getIsRestricted()
+                            ? new Face("" /* TODO */, faceId, getHalDeviceId())
+                            : null;
+                    receiver.onAuthenticationSucceeded(getHalDeviceId(), face);
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify Authenticated:", e);
+                result = true; // client failed
+            }
+        } else {
+            result = true; // client not listening
+        }
+        if (!authenticated) {
+            // allow system-defined limit of number of attempts before giving up
+            int lockoutMode =  handleFailedAttempt();
+            if (lockoutMode != LOCKOUT_NONE) {
+                try {
+                    Slog.w(TAG, "Forcing lockout (face authentication driver code should do " +
+                            "this!), mode(" + lockoutMode + ")");
+                    stop(false);
+                    int errorCode = lockoutMode == LOCKOUT_TIMED ?
+                            FaceManager.FACE_ERROR_LOCKOUT :
+                            FaceManager.FACE_ERROR_LOCKOUT_PERMANENT;
+                    receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to notify lockout:", e);
+                }
+            }
+            result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
+        } else {
+            if (receiver != null) {
+                vibrateSuccess();
+            }
+            result |= true; // we have a valid face, done
+            resetFailedAttempts();
+        }
+        return result;
+    }
+
+    /**
+     * Start authentication
+     */
+    @Override
+    public int start() {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "start authentication: no face HAL!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.authenticate(mOpId);
+            if (result != 0) {
+                Slog.w(TAG, "startAuthentication failed, result=" + result);
+                MetricsLogger.histogram(getContext(), "faced_auth_start_error", result);
+                onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(TAG, "stopAuthentication: already cancelled!");
+            return 0;
+        }
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopAuthentication: no face HAL!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancel();
+            if (result != 0) {
+                Slog.w(TAG, "stopAuthentication failed, result=" + result);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        mAlreadyCancelled = true;
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnrollResult(int faceId, int remaining) {
+        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onRemoved(int faceId, int remaining) {
+        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/ClientMonitor.java b/services/core/java/com/android/server/biometrics/face/ClientMonitor.java
new file mode 100644
index 0000000..4b13db9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/ClientMonitor.java
@@ -0,0 +1,243 @@
+/**
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.Manifest;
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.util.Slog;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract base class for keeping track and dispatching events from face HAL to the
+ * current client.  Subclasses are responsible for coordinating the interaction with
+ * face HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ */
+public abstract class ClientMonitor implements IBinder.DeathRecipient {
+    protected static final String TAG = FaceService.TAG; // TODO: get specific name
+    protected static final int ERROR_ESRCH = 3; // Likely face HAL is dead. See errno.h.
+    protected static final boolean DEBUG = FaceService.DEBUG;
+    private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
+    private final Context mContext;
+    private final long mHalDeviceId;
+    private final int mTargetUserId;
+    // True if client does not have MANAGE_FACE_AUTHENTICATION permission
+    private final boolean mIsRestricted;
+    private final String mOwner;
+    private final VibrationEffect mSuccessVibrationEffect;
+    private IBinder mToken;
+    private IFaceServiceReceiver mReceiver;
+    protected boolean mAlreadyCancelled;
+
+    /**
+     * @param context context of FaceService
+     * @param halDeviceId the HAL device ID of the associated face authentication hardware
+     * @param token a unique token for the client
+     * @param receiver recipient of related events (e.g. authentication)
+     * @param userId target user id for operation
+     * @param restricted whether or not client has the {@link Manifest#MANAGE_FACE}
+     * permission
+     * @param owner name of the client that owns this
+     */
+    public ClientMonitor(Context context, long halDeviceId, IBinder token,
+            IFaceServiceReceiver receiver, int userId, boolean restricted,
+            String owner) {
+        mContext = context;
+        mHalDeviceId = halDeviceId;
+        mToken = token;
+        mReceiver = receiver;
+        mTargetUserId = userId;
+        mIsRestricted = restricted;
+        mOwner = owner;
+        mSuccessVibrationEffect = getSuccessVibrationEffect(context);
+        try {
+            if (token != null) {
+                token.linkToDeath(this, 0);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+        }
+    }
+
+    /**
+     * Contacts face HAL to start the client.
+     * @return 0 on success, errno from driver on failure
+     */
+    public abstract int start();
+
+    /**
+     * Contacts face HAL to stop the client.
+     * @param initiatedByClient whether the operation is at the request of a client
+     */
+    public abstract int stop(boolean initiatedByClient);
+
+    /**
+     * Method to explicitly poke powermanager on events
+     */
+    public abstract void notifyUserActivity();
+
+    /**
+     * Gets the face daemon from the cached state in the container class.
+     */
+    public abstract IBiometricsFace getFaceDaemon();
+
+    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
+    // respective client (e.g. enrolling shouldn't get authenticate events).
+    // All of these return 'true' if the operation is completed and it's ok to move
+    // to the next client (e.g. authentication accepts or rejects a face).
+    public abstract boolean onEnrollResult(int faceId, int remaining);
+    public abstract boolean onAuthenticated(int faceId);
+    public abstract boolean onRemoved(int faceId, int remaining);
+
+    /**
+     * Called when we get notification from face HAL that an image has been acquired.
+     * Common to authenticate and enroll.
+     * @param acquiredInfo info about the current image acquisition
+     * @return true if client should be removed
+     */
+    public boolean onAcquired(int acquiredInfo, int vendorCode) {
+        if (mReceiver == null)
+            return true; // client not connected
+        try {
+            mReceiver.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
+            return false; // acquisition continues...
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to invoke sendAcquired:", e);
+            return true; // client failed
+        } finally {
+            // Good scans will keep the device awake
+            if (acquiredInfo == FaceManager.FACE_ACQUIRED_GOOD) {
+                notifyUserActivity();
+            }
+        }
+    }
+
+    /**
+     * Called when we get notification from face HAL that an error has occurred with the
+     * current operation. Common to authenticate, enroll, enumerate and remove.
+     * @param error
+     * @return true if client should be removed
+     */
+    public boolean onError(int error, int vendorCode) {
+        if (mReceiver != null) {
+            try {
+                mReceiver.onError(getHalDeviceId(), error, vendorCode);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke sendError:", e);
+            }
+        }
+        return true; // errors always remove current client
+    }
+
+    public void destroy() {
+        if (mToken != null) {
+            try {
+                mToken.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                // TODO: remove when duplicate call bug is found
+                Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+            }
+            mToken = null;
+        }
+        mReceiver = null;
+    }
+
+    @Override
+    public void binderDied() {
+        mToken = null;
+        mReceiver = null;
+        onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mToken != null) {
+                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
+                onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public final Context getContext() {
+        return mContext;
+    }
+
+    public final long getHalDeviceId() {
+        return mHalDeviceId;
+    }
+
+    public final String getOwnerString() {
+        return mOwner;
+    }
+
+    public final IFaceServiceReceiver getReceiver() {
+        return mReceiver;
+    }
+
+    public final boolean getIsRestricted() {
+        return mIsRestricted;
+    }
+
+    public final int getTargetUserId() {
+        return mTargetUserId;
+    }
+
+    public final IBinder getToken() {
+        return mToken;
+    }
+
+    public final void vibrateSuccess() {
+        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+        if (vibrator != null) {
+            vibrator.vibrate(mSuccessVibrationEffect);
+        }
+    }
+
+//    public final void vibrateError() {
+//    }
+
+    private static VibrationEffect getSuccessVibrationEffect(Context ctx) {
+        int[] arr = ctx.getResources().getIntArray(
+                com.android.internal.R.array.config_longPressVibePattern);
+        final long[] vibePattern;
+        if (arr == null || arr.length == 0) {
+            vibePattern = DEFAULT_SUCCESS_VIBRATION_PATTERN;
+        } else {
+            vibePattern = new long[arr.length];
+            for (int i = 0; i < arr.length; i++) {
+                vibePattern[i] = arr[i];
+            }
+        }
+        if (vibePattern.length == 1) {
+            return VibrationEffect.createOneShot(
+                    vibePattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
+        } else {
+            return VibrationEffect.createWaveform(vibePattern, -1);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/face/EnrollClient.java b/services/core/java/com/android/server/biometrics/face/EnrollClient.java
new file mode 100644
index 0000000..91e489a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/EnrollClient.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+    private static final long MS_PER_SEC = 1000;
+    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+    private byte[] mCryptoToken;
+
+    public EnrollClient(Context context, long halDeviceId, IBinder token,
+            IFaceServiceReceiver receiver, int userId, byte [] cryptoToken, boolean restricted,
+            String owner) {
+        super(context, halDeviceId, token, receiver, userId, restricted, owner);
+        mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+    }
+
+    @Override
+    public boolean onEnrollResult(int faceId, int remaining) {
+        if (remaining == 0) {
+            FaceUtils.getInstance().addFaceForUser(getContext(), faceId, getTargetUserId());
+        }
+        return sendEnrollResult(faceId, remaining);
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendEnrollResult(int faceId, int remaining) {
+        IFaceServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+
+        vibrateSuccess();
+        MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
+        // TODO: create ACTION_FACE_ENROLL constant and use it here
+        try {
+            receiver.onEnrollResult(getHalDeviceId(), faceId, remaining);
+            return remaining == 0;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify EnrollResult:", e);
+            return true;
+        }
+    }
+
+    @Override
+    public int start() {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "enroll: no face HAL!");
+            return ERROR_ESRCH;
+        }
+        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+        try {
+            // ugh...
+            final ArrayList<Byte> token = new ArrayList<>();
+            for (int i = 0; i < mCryptoToken.length; i++) {
+                token.add(mCryptoToken[i]);
+            }
+            final int result = daemon.enroll(token, timeout);
+            if (result != 0) {
+                Slog.w(TAG, "startEnroll failed, result=" + result);
+                MetricsLogger.histogram(getContext(), "faced_enroll_start_error", result);
+                onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startEnroll failed", e);
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(TAG, "stopEnroll: already cancelled!");
+            return 0;
+        }
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopEnrollment: no face HAL!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancel();
+            if (result != 0) {
+                Slog.w(TAG, "startEnrollCancel failed, result = " + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopEnrollment failed", e);
+        }
+        if (initiatedByClient) {
+            onError(FaceManager.FACE_ERROR_CANCELED, 0 /* vendorCode */);
+        }
+        mAlreadyCancelled = true;
+        return 0;
+    }
+
+    @Override
+    public boolean onRemoved(int faceId, int remaining) {
+        if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onAuthenticated(int faceId) {
+        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
new file mode 100644
index 0000000..a9a5ad0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -0,0 +1,1254 @@
+/*
+ * Copyright (C) 2018 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.biometrics.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.RESET_FACE_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.app.SynchronousUserSwitchObserver;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
+import android.hardware.face.IFaceServiceLockoutResetCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.KeyStore;
+import android.service.face.FaceActionStatsProto;
+import android.service.face.FaceServiceDumpProto;
+import android.service.face.FaceUserStatsProto;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.SystemService;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A service to manage multiple clients that want to access the face HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * face -related events.
+ *
+ * @hide
+ */
+public class FaceService extends SystemService implements IHwBinder.DeathRecipient {
+    static final String TAG = "FaceService";
+    static final boolean DEBUG = true;
+    private static final String FACE_DATA_DIR = "facedata";
+    private static final int MSG_USER_SWITCHING = 10;
+    private static final String ACTION_LOCKOUT_RESET =
+            "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
+
+    private class PerformanceStats {
+        int accept; // number of accepted faces
+        int reject; // number of rejected faces
+        int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
+                     // acquisition in some cases (too high, too low, poor gaze, etc.)
+        int lockout; // total number of lockouts
+        int permanentLockout; // total number of permanent lockouts
+    }
+
+    private final ArrayList<FaceServiceLockoutResetMonitor> mLockoutMonitors =
+            new ArrayList<>();
+    private final Map<Integer, Long> mAuthenticatorIds =
+            Collections.synchronizedMap(new HashMap<>());
+    private final AppOpsManager mAppOps;
+    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
+    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
+    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+
+    private static final long CANCEL_TIMEOUT_LIMIT_MS = 3000; // max wait for onCancel() from HAL,in ms
+    private final String mKeyguardPackage;
+    private int mCurrentUserId = UserHandle.USER_NULL;
+    private final FaceUtils mFaceUtils = FaceUtils.getInstance();
+    private Context mContext;
+    private long mHalDeviceId;
+    private boolean mTimedLockoutCleared;
+    private int mFailedAttempts;
+    @GuardedBy("this")
+    private IBiometricsFace mDaemon;
+    private final PowerManager mPowerManager;
+    private final AlarmManager mAlarmManager;
+    private final UserManager mUserManager;
+    private ClientMonitor mCurrentClient;
+    private ClientMonitor mPendingClient;
+    private PerformanceStats mPerformanceStats;
+
+
+    private IBinder mToken = new Binder(); // used for internal FaceService enumeration
+
+    private class UserFace {
+        Face f;
+        int userId;
+        public UserFace(Face f, int userId) {
+            this.f = f;
+            this.userId = userId;
+        }
+    }
+
+    // Normal face authentications are tracked by mPerformanceMap.
+    private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
+
+    // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+    private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(android.os.Message msg) {
+            switch (msg.what) {
+                case MSG_USER_SWITCHING:
+                    handleUserSwitching(msg.arg1);
+                    break;
+
+                default:
+                    Slog.w(TAG, "Unknown message:" + msg.what);
+            }
+        }
+    };
+
+    private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
+                resetFailedAttempts(false /* clearAttemptCounter */);
+            }
+        }
+    };
+
+    private final Runnable mResetFailedAttemptsRunnable = new Runnable() {
+        @Override
+        public void run() {
+            resetFailedAttempts(true /* clearAttemptCounter */);
+        }
+    };
+
+    private final Runnable mResetClientState = new Runnable() {
+        @Override
+        public void run() {
+            // Warning: if we get here, the driver never confirmed our call to cancel the current
+            // operation (authenticate, enroll, remove, enumerate, etc), which is
+            // really bad.  The result will be a 3-second delay in starting each new client.
+            // If you see this on a device, make certain the driver notifies with
+            // {@link FaceAuthenticationManager#FACE_ERROR_CANCEL} in response to cancel()
+            // once it has successfully switched to the IDLE state in the face HAL.
+            // Additionally,{@link FaceAuthenticationManager#FACE_ERROR_CANCEL} should only be sent
+            // in response to an actual cancel() call.
+            Slog.w(TAG, "Client "
+                    + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+                    + " failed to respond to cancel, starting client "
+                    + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+
+            mCurrentClient = null;
+            startClient(mPendingClient, false);
+        }
+    };
+
+    public FaceService(Context context) {
+        super(context);
+        mContext = context;
+        mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+                com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+        mAppOps = context.getSystemService(AppOpsManager.class);
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
+                RESET_FACE_LOCKOUT, null /* handler */);
+        mUserManager = UserManager.get(mContext);
+    }
+
+    @Override
+    public void serviceDied(long cookie) {
+        Slog.v(TAG, "face HAL died");
+        MetricsLogger.count(mContext, "faced_died", 1);
+        handleError(mHalDeviceId, FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+                0 /*vendorCode */);
+    }
+
+    public synchronized IBiometricsFace getFaceDaemon() {
+        if (mDaemon == null) {
+            Slog.v(TAG, "mDaemon was null, reconnect to face");
+            try {
+                mDaemon = IBiometricsFace.getService();
+            } catch (java.util.NoSuchElementException e) {
+                // Service doesn't exist or cannot be opened. Logged below.
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get biometric interface", e);
+            }
+            if (mDaemon == null) {
+                Slog.w(TAG, "face HIDL not available");
+                return null;
+            }
+
+            mDaemon.asBinder().linkToDeath(this, 0);
+
+            try {
+                mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to open face HAL", e);
+                mDaemon = null; // try again later!
+            }
+
+            if (DEBUG) Slog.v(TAG, "face HAL id: " + mHalDeviceId);
+            if (mHalDeviceId != 0) {
+                loadAuthenticatorIds();
+                updateActiveGroup(ActivityManager.getCurrentUser(), null);
+            } else {
+                Slog.w(TAG, "Failed to open Face HAL!");
+                MetricsLogger.count(mContext, "faced_openhal_error", 1);
+                mDaemon = null;
+            }
+        }
+        return mDaemon;
+    }
+
+    /** Populates existing authenticator ids. To be used only during the start of the service. */
+    private void loadAuthenticatorIds() {
+        // This operation can be expensive, so keep track of the elapsed time. Might need to move to
+        // background if it takes too long.
+        long t = System.currentTimeMillis();
+        mAuthenticatorIds.clear();
+        for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+            int userId = getUserOrWorkProfileId(null, user.id);
+            if (!mAuthenticatorIds.containsKey(userId)) {
+                updateActiveGroup(userId, null);
+            }
+        }
+
+        t = System.currentTimeMillis() - t;
+        if (t > 1000) {
+            Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
+        }
+    }
+
+    protected void handleError(long deviceId, int error, int vendorCode) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onError(error, vendorCode)) {
+            removeClient(client);
+        }
+
+        if (DEBUG) Slog.v(TAG, "handleError(client="
+                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+        // This is the magic code that starts the next client when the old client finishes.
+        if (error == FaceManager.FACE_ERROR_CANCELED) {
+            mHandler.removeCallbacks(mResetClientState);
+            if (mPendingClient != null) {
+                if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
+                startClient(mPendingClient, false);
+                mPendingClient = null;
+            }
+        } else if (error == FaceManager.FACE_ERROR_HW_UNAVAILABLE) {
+            // If we get HW_UNAVAILABLE, try to connect again later...
+            Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+            synchronized (this) {
+                mDaemon = null;
+                mHalDeviceId = 0;
+                mCurrentUserId = UserHandle.USER_NULL;
+            }
+        }
+    }
+
+    protected void handleRemoved(long deviceId, int faceId, int userId, int remaining) {
+        if (DEBUG) Slog.w(TAG, "Removed: fid=" + faceId
+                + ", uid=" + userId
+                + ", dev=" + deviceId
+                + ", rem=" + remaining);
+
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onRemoved(faceId, remaining)) {
+            removeClient(client);
+        }
+    }
+
+    protected void handleAuthenticated(long deviceId, int faceId, int userId,
+            ArrayList<Byte> token) {
+        ClientMonitor client = mCurrentClient;
+        if (faceId != 0) {
+            // Ugh...
+            final byte[] byteToken = new byte[token.size()];
+            for (int i = 0; i < token.size(); i++) {
+                byteToken[i] = token.get(i);
+            }
+            // Send to Keystore
+            KeyStore.getInstance().addAuthToken(byteToken);
+        }
+        if (client != null && client.onAuthenticated(faceId)) {
+            removeClient(client);
+        }
+        if (faceId != 0) {
+            mPerformanceStats.accept++;
+        } else {
+            mPerformanceStats.reject++;
+        }
+    }
+
+    protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
+            removeClient(client);
+        }
+        if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+                && client instanceof AuthenticationClient) {
+            // ignore enrollment acquisitions or acquisitions when we're locked out
+            mPerformanceStats.acquire++;
+        }
+    }
+
+    protected void handleEnrollResult(long deviceId, int faceId, int userId, int remaining) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onEnrollResult(faceId, remaining)) {
+            removeClient(client);
+        }
+    }
+
+    private void userActivity() {
+        long now = SystemClock.uptimeMillis();
+        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+    }
+
+    void handleUserSwitching(int userId) {
+        updateActiveGroup(userId, null);
+    }
+
+    private void removeClient(ClientMonitor client) {
+        if (client != null) {
+            client.destroy();
+            if (client != mCurrentClient && mCurrentClient != null) {
+                Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
+                        + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
+            }
+        }
+        if (mCurrentClient != null) {
+            if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
+            mCurrentClient = null;
+        }
+    }
+
+    private int getLockoutMode() {
+        if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+            return AuthenticationClient.LOCKOUT_PERMANENT;
+        } else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
+                (mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+            return AuthenticationClient.LOCKOUT_TIMED;
+        }
+        return AuthenticationClient.LOCKOUT_NONE;
+    }
+
+    private void scheduleLockoutReset() {
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent());
+    }
+
+    private void cancelLockoutReset() {
+        mAlarmManager.cancel(getLockoutResetIntent());
+    }
+
+    private PendingIntent getLockoutResetIntent() {
+        return PendingIntent.getBroadcast(mContext, 0,
+                new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    public long startPreEnroll(IBinder token) {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startPreEnroll: no face HAL!");
+            return 0;
+        }
+        try {
+            return daemon.preEnroll().value;
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startPreEnroll failed", e);
+        }
+        return 0;
+    }
+
+    public int startPostEnroll(IBinder token) {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startPostEnroll: no face HAL!");
+            return 0;
+        }
+        try {
+            return daemon.postEnroll();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startPostEnroll failed", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Calls face HAL to switch states to the new task. If there's already a current task,
+     * it calls cancel() and sets mPendingClient to begin when the current task finishes
+     * ({@link FaceManager#FACE_ERROR_CANCELED}).
+     * @param newClient the new client that wants to connect
+     * @param initiatedByClient true for authenticate, remove and enroll
+     */
+    private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+        ClientMonitor currentClient = mCurrentClient;
+        if (currentClient != null) {
+            if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
+            if (currentClient instanceof InternalRemovalClient) {
+                // This condition means we're currently running internal diagnostics to
+                // remove a face in the hardware and/or the software
+                // TODO: design an escape hatch in case client never finishes
+            }
+            else {
+                currentClient.stop(initiatedByClient);
+            }
+            mPendingClient = newClient;
+            mHandler.removeCallbacks(mResetClientState);
+            mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT_MS);
+        } else if (newClient != null) {
+            mCurrentClient = newClient;
+            if (DEBUG) Slog.v(TAG, "starting client "
+                    + newClient.getClass().getSuperclass().getSimpleName()
+                    + "(" + newClient.getOwnerString() + ")"
+                    + ", initiatedByClient = " + initiatedByClient + ")");
+            newClient.start();
+        }
+    }
+
+    void startRemove(IBinder token, int userId, IFaceServiceReceiver receiver, boolean restricted,
+                     boolean internal) {
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "startRemove: no face HAL!");
+            return;
+        }
+
+        if (internal) {
+            Context context = getContext();
+            InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
+                    token, receiver, userId, restricted, context.getOpPackageName()) {
+                @Override
+                public void notifyUserActivity() {
+
+                }
+                @Override
+                public IBiometricsFace getFaceDaemon() {
+                    return FaceService.this.getFaceDaemon();
+                }
+            };
+            startClient(client, true);
+        }
+        else {
+            RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
+                    receiver, userId, restricted, token.toString()) {
+                @Override
+                public void notifyUserActivity() {
+                    FaceService.this.userActivity();
+                }
+
+                @Override
+                public IBiometricsFace getFaceDaemon() {
+                    return FaceService.this.getFaceDaemon();
+                }
+            };
+            startClient(client, true);
+        }
+    }
+
+    /*
+     * @hide
+     */
+    public Face getEnrolledFace(int userId) {
+        return mFaceUtils.getFaceForUser(mContext, userId);
+    }
+
+    public boolean hasEnrolledFace(int userId) {
+        if (userId != UserHandle.getCallingUserId()) {
+            checkPermission(INTERACT_ACROSS_USERS);
+        }
+        return mFaceUtils.getFaceForUser(mContext, userId) != null;
+    }
+
+    boolean hasPermission(String permission) {
+        return getContext().checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    void checkPermission(String permission) {
+        getContext().enforceCallingOrSelfPermission(permission,
+                "Must have " + permission + " permission.");
+    }
+
+    int getEffectiveUserId(int userId) {
+        UserManager um = UserManager.get(mContext);
+        if (um != null) {
+            final long callingIdentity = Binder.clearCallingIdentity();
+            userId = um.getCredentialOwnerProfile(userId);
+            Binder.restoreCallingIdentity(callingIdentity);
+        } else {
+            Slog.e(TAG, "Unable to acquire UserManager");
+        }
+        return userId;
+    }
+
+    boolean isCurrentUserOrProfile(int userId) {
+        UserManager um = UserManager.get(mContext);
+        if (um == null) {
+            Slog.e(TAG, "Unable to acquire UserManager");
+            return false;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // Allow current user or profiles of the current user...
+            for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+                if (profileId == userId) {
+                    return true;
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        return false;
+    }
+
+    private boolean isForegroundActivity(int uid, int pid) {
+        try {
+            List<RunningAppProcessInfo> procs =
+                    ActivityManager.getService().getRunningAppProcesses();
+            int N = procs.size();
+            for (int i = 0; i < N; i++) {
+                RunningAppProcessInfo proc = procs.get(i);
+                if (proc.pid == pid && proc.uid == uid
+                        && proc.importance == IMPORTANCE_FOREGROUND) {
+                    return true;
+                }
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "am.getRunningAppProcesses() failed");
+        }
+        return false;
+    }
+
+    /**
+     * @param opPackageName name of package for caller
+     * @param requireForeground only allow this call while app is in the foreground
+     * @return true if caller can use face API
+     */
+    private boolean canUseFace(String opPackageName, boolean requireForeground, int uid,
+            int pid, int userId) {
+        checkPermission(USE_BIOMETRIC);
+        if (isKeyguard(opPackageName)) {
+            return true; // Keyguard is always allowed
+        }
+        if (!isCurrentUserOrProfile(userId)) {
+            Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
+            return false;
+        }
+        if (mAppOps.noteOp(AppOpsManager.OP_USE_FACE, uid, opPackageName)
+                != AppOpsManager.MODE_ALLOWED) {
+            Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
+            return false;
+        }
+        if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
+            Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @param opPackageName package of the caller
+     * @return true if this is the same client currently using face
+     */
+    private boolean currentClient(String opPackageName) {
+        return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+    }
+
+    /**
+     * @param clientPackage
+     * @return true if this is keyguard package
+     */
+    private boolean isKeyguard(String clientPackage) {
+        return mKeyguardPackage.equals(clientPackage);
+    }
+
+    private void addLockoutResetMonitor(FaceServiceLockoutResetMonitor monitor) {
+        if (!mLockoutMonitors.contains(monitor)) {
+            mLockoutMonitors.add(monitor);
+        }
+    }
+
+    private void removeLockoutResetCallback(
+            FaceServiceLockoutResetMonitor monitor) {
+        mLockoutMonitors.remove(monitor);
+    }
+
+    private void notifyLockoutResetMonitors() {
+        for (int i = 0; i < mLockoutMonitors.size(); i++) {
+            mLockoutMonitors.get(i).sendLockoutReset();
+        }
+    }
+
+    private void startAuthentication(IBinder token, long opId, int callingUserId,
+                IFaceServiceReceiver receiver, int flags, boolean restricted,
+                String opPackageName) {
+        updateActiveGroup(callingUserId, opPackageName);
+
+        if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
+
+        AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
+                receiver, mCurrentUserId, opId, restricted, opPackageName) {
+            @Override
+            public int handleFailedAttempt() {
+                mFailedAttempts++;
+                mTimedLockoutCleared = false;
+                final int lockoutMode = getLockoutMode();
+                if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+                    mPerformanceStats.permanentLockout++;
+                } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+                    mPerformanceStats.lockout++;
+                }
+
+                // Failing multiple times will continue to push out the lockout time
+                if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+                    scheduleLockoutReset();
+                    return lockoutMode;
+                }
+                return AuthenticationClient.LOCKOUT_NONE;
+            }
+
+            @Override
+            public void resetFailedAttempts() {
+                FaceService.this.resetFailedAttempts(true /* clearAttemptCounter */);
+            }
+
+            @Override
+            public void notifyUserActivity() {
+                FaceService.this.userActivity();
+            }
+
+            @Override
+            public IBiometricsFace getFaceDaemon() {
+                return FaceService.this.getFaceDaemon();
+            }
+        };
+
+        int lockoutMode = getLockoutMode();
+        if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+            Slog.v(TAG, "In lockout mode(" + lockoutMode +
+                    ") ; disallowing authentication");
+            int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
+                    FaceManager.FACE_ERROR_LOCKOUT :
+                    FaceManager.FACE_ERROR_LOCKOUT_PERMANENT;
+            if (!client.onError(errorCode, 0 /* vendorCode */)) {
+                Slog.w(TAG, "Cannot send permanent lockout message to client");
+            }
+            return;
+        }
+        startClient(client, true /* initiatedByClient */);
+    }
+
+    private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
+            IFaceServiceReceiver receiver, int flags, boolean restricted,
+            String opPackageName) {
+        updateActiveGroup(userId, opPackageName);
+
+        EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
+                userId, cryptoToken, restricted, opPackageName) {
+
+            @Override
+            public IBiometricsFace getFaceDaemon() {
+                return FaceService.this.getFaceDaemon();
+            }
+
+            @Override
+            public void notifyUserActivity() {
+                FaceService.this.userActivity();
+            }
+        };
+        startClient(client, true /* initiatedByClient */);
+    }
+
+    // attempt counter should only be cleared when Keyguard goes away or when
+    // a face is successfully authenticated
+    protected void resetFailedAttempts(boolean clearAttemptCounter) {
+        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+            Slog.v(TAG, "Reset face lockout, clearAttemptCounter=" + clearAttemptCounter);
+        }
+        if (clearAttemptCounter) {
+            mFailedAttempts = 0;
+        }
+        mTimedLockoutCleared = true;
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+        // the alarm might still be pending; remove it.
+        cancelLockoutReset();
+        notifyLockoutResetMonitors();
+    }
+
+    private class FaceServiceLockoutResetMonitor {
+
+        private static final long WAKELOCK_TIMEOUT_MS = 2000;
+        private final IFaceServiceLockoutResetCallback mCallback;
+        private final WakeLock mWakeLock;
+
+        public FaceServiceLockoutResetMonitor(
+                IFaceServiceLockoutResetCallback callback) {
+            mCallback = callback;
+            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "lockout reset callback");
+        }
+
+        public void sendLockoutReset() {
+            if (mCallback != null) {
+                try {
+                    mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+                    mCallback.onLockoutReset(mHalDeviceId, new IRemoteCallback.Stub() {
+
+                        @Override
+                        public void sendResult(Bundle data) throws RemoteException {
+                            if (mWakeLock.isHeld()) {
+                                mWakeLock.release();
+                            }
+                        }
+                    });
+                } catch (DeadObjectException e) {
+                    Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
+                    mHandler.post(mRemoveCallbackRunnable);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+                }
+            }
+        }
+
+        private final Runnable mRemoveCallbackRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mWakeLock.isHeld()) {
+                    mWakeLock.release();
+                }
+                removeLockoutResetCallback(FaceServiceLockoutResetMonitor.this);
+            }
+        };
+    }
+
+    private IBiometricsFaceClientCallback mDaemonCallback =
+            new IBiometricsFaceClientCallback.Stub() {
+
+        @Override
+        public void onEnrollResult(final long deviceId, int faceId, int userId, int remaining) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleEnrollResult(deviceId, faceId, userId, remaining);
+                }
+            });
+        }
+
+        @Override
+        public void onAcquired(final long deviceId, final int userId, final int acquiredInfo,
+                final int vendorCode) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleAcquired(deviceId, acquiredInfo, vendorCode);
+                }
+            });
+        }
+
+        @Override
+        public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+                ArrayList<Byte> token) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleAuthenticated(deviceId, faceId, userId, token);
+                }
+            });
+        }
+
+        @Override
+        public void onError(final long deviceId, final int userId, final int error,
+                final int vendorCode) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleError(deviceId, error, vendorCode);
+                }
+            });
+        }
+
+        @Override
+        public void onRemoved(final long deviceId, final int faceId, final int userId,
+                final int remaining) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleRemoved(deviceId, faceId, userId, remaining);
+                }
+            });
+        }
+
+        @Override
+        public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+                throws RemoteException {
+
+        }
+    };
+
+    private final class FaceServiceWrapper extends IFaceService.Stub {
+        @Override // Binder call
+        public long preEnroll(IBinder token) {
+            checkPermission(MANAGE_FACE);
+            return startPreEnroll(token);
+        }
+
+        @Override // Binder call
+        public int postEnroll(IBinder token) {
+            checkPermission(MANAGE_FACE);
+            return startPostEnroll(token);
+        }
+
+        @Override // Binder call
+        public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+                final IFaceServiceReceiver receiver, final int flags,
+                final String opPackageName) {
+            checkPermission(MANAGE_FACE);
+
+            Face enrolledFace = FaceService.this.getEnrolledFace(userId);
+
+            if (enrolledFace != null) {
+                Slog.w(TAG, "Multiple faces enrollment is not supported");
+                return;
+            }
+
+
+            final boolean restricted = isRestricted();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    startEnrollment(token, cryptoToken, userId, receiver, flags,
+                            restricted, opPackageName);
+                }
+            });
+        }
+
+        private boolean isRestricted() {
+            // Only give privileged apps (like Settings) access to faces info
+            final boolean restricted = !hasPermission(MANAGE_FACE);
+            return restricted;
+        }
+
+        @Override // Binder call
+        public void cancelEnrollment(final IBinder token) {
+            checkPermission(MANAGE_FACE);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientMonitor client = mCurrentClient;
+                    if (client instanceof EnrollClient && client.getToken() == token) {
+                        client.stop(client.getToken() == token);
+                    }
+                }
+            });
+        }
+
+        @Override // Binder call
+        public void authenticate(final IBinder token, final long opId,
+                                 final IFaceServiceReceiver receiver, final int flags,
+                                 final String opPackageName) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingUserId = UserHandle.getCallingUserId();
+            final int pid = Binder.getCallingPid();
+            final boolean restricted = isRestricted();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (!canUseFace(opPackageName, true /* foregroundOnly */,
+                            callingUid, pid, callingUserId)) {
+                        if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
+                        return;
+                    }
+
+                    MetricsLogger.histogram(mContext, "faces_token", opId != 0L ? 1 : 0);
+
+                    // Get performance stats object for this user.
+                    HashMap<Integer, PerformanceStats> pmap
+                            = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+                    PerformanceStats stats = pmap.get(mCurrentUserId);
+                    if (stats == null) {
+                        stats = new PerformanceStats();
+                        pmap.put(mCurrentUserId, stats);
+                    }
+                    mPerformanceStats = stats;
+
+                    startAuthentication(token, opId, callingUserId, receiver,
+                            flags, restricted, opPackageName);
+                }
+            });
+        }
+
+        @Override // Binder call
+        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+            final int uid = Binder.getCallingUid();
+            final int pid = Binder.getCallingPid();
+            final int callingUserId = UserHandle.getCallingUserId();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (!canUseFace(opPackageName, true /* foregroundOnly */, uid, pid,
+                            callingUserId)) {
+                        if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
+                    } else {
+                        ClientMonitor client = mCurrentClient;
+                        if (client instanceof AuthenticationClient) {
+                            if (client.getToken() == token) {
+                                if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
+                                client.stop(client.getToken() == token);
+                            } else {
+                                if (DEBUG) Slog.v(TAG, "can't stop client "
+                                        + client.getOwnerString() + " since tokens don't match");
+                            }
+                        } else if (client != null) {
+                            if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
+                                    + client.getOwnerString());
+                        }
+                    }
+                }
+            });
+        }
+
+        @Override // Binder call
+        public void setActiveUser(final int userId) {
+            checkPermission(MANAGE_FACE);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    updateActiveGroup(userId, null);
+                }
+            });
+        }
+
+        @Override // Binder call
+        public void remove(final IBinder token, final int userId,
+                           final IFaceServiceReceiver receiver) {
+            checkPermission(MANAGE_FACE); // TODO: Maybe have another permission
+            final boolean restricted = isRestricted();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    startRemove(token, userId, receiver, restricted, false /* internal */);
+                }
+            });
+        }
+
+        @Override // Binder call
+        public boolean isHardwareDetected(long deviceId, String opPackageName) {
+            if (!canUseFace(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return false;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                IBiometricsFace daemon = getFaceDaemon();
+                return daemon != null && mHalDeviceId != 0;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public Face getEnrolledFace(int userId, String opPackageName) {
+            if (!canUseFace(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return null;
+            }
+
+            return FaceService.this.getEnrolledFace(userId);
+        }
+
+        @Override // Binder call
+        public boolean hasEnrolledFace(int userId, String opPackageName) {
+            if (!canUseFace(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
+                return false;
+            }
+
+            return FaceService.this.hasEnrolledFace(userId);
+        }
+
+        @Override // Binder call
+        public long getAuthenticatorId(String opPackageName) {
+            // In this method, we're not checking whether the caller is permitted to use face
+            // API because current authenticator ID is leaked (in a more contrived way) via Android
+            // Keystore (android.security.keystore package): the user of that API can create a key
+            // which requires face authentication for its use, and then query the key's
+            // characteristics (hidden API) which returns, among other things, face
+            // authenticator ID which was active at key creation time.
+            //
+            // Reason: The part of Android Keystore which runs inside an app's process invokes this
+            // method in certain cases. Those cases are not always where the developer demonstrates
+            // explicit intent to use face functionality. Thus, to avoiding throwing an
+            // unexpected SecurityException this method does not check whether its caller is
+            // permitted to use face API.
+            //
+            // The permission check should be restored once Android Keystore no longer invokes this
+            // method from inside app processes.
+
+            return FaceService.this.getAuthenticatorId(opPackageName);
+        }
+
+        @Override // Binder call
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (args.length > 0 && "--proto".equals(args[0])) {
+                    dumpProto(fd);
+                } else {
+                    dumpInternal(pw);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        public void resetTimeout(byte [] token) {
+            checkPermission(RESET_FACE_LOCKOUT);
+            // TODO: confirm security token when we move timeout management into the HAL layer.
+            mHandler.post(mResetFailedAttemptsRunnable);
+        }
+
+        @Override
+        public void addLockoutResetCallback(final IFaceServiceLockoutResetCallback callback)
+                throws RemoteException {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    addLockoutResetMonitor(
+                            new FaceServiceLockoutResetMonitor(callback));
+                }
+            });
+        }
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        JSONObject dump = new JSONObject();
+        try {
+            dump.put("service", "Face Manager");
+
+            JSONArray sets = new JSONArray();
+            for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+                final int userId = user.getUserHandle().getIdentifier();
+                PerformanceStats stats = mPerformanceMap.get(userId);
+                PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+                JSONObject set = new JSONObject();
+                set.put("id", userId);
+                set.put("accept", (stats != null) ? stats.accept : 0);
+                set.put("reject", (stats != null) ? stats.reject : 0);
+                set.put("acquire", (stats != null) ? stats.acquire : 0);
+                set.put("lockout", (stats != null) ? stats.lockout : 0);
+                set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+                // cryptoStats measures statistics about secure face transactions
+                // (e.g. to unlock password storage, make secure purchases, etc.)
+                set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+                set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+                set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+                set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+                sets.put(set);
+            }
+
+            dump.put("prints", sets);
+        } catch (JSONException e) {
+            Slog.e(TAG, "dump formatting failure", e);
+        }
+        pw.println(dump);
+    }
+
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+
+            final long userToken = proto.start(FaceServiceDumpProto.USERS);
+
+            proto.write(FaceUserStatsProto.USER_ID, userId);
+
+            // Normal face authentications (e.g. lockscreen)
+            final PerformanceStats normal = mPerformanceMap.get(userId);
+            if (normal != null) {
+                final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
+                proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
+                proto.write(FaceActionStatsProto.REJECT, normal.reject);
+                proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
+                proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
+                proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
+                proto.end(countsToken);
+            }
+
+            // Statistics about secure face transactions (e.g. to unlock password
+            // storage, make secure purchases, etc.)
+            final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+            if (crypto != null) {
+                final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
+                proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
+                proto.write(FaceActionStatsProto.REJECT, crypto.reject);
+                proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
+                proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
+                proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
+                proto.end(countsToken);
+            }
+
+            proto.end(userToken);
+        }
+        proto.flush();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
+        SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
+        listenForUserSwitches();
+    }
+
+    private void updateActiveGroup(int userId, String clientPackage) {
+        IBiometricsFace daemon = getFaceDaemon();
+
+        if (daemon != null) {
+            try {
+                userId = getUserOrWorkProfileId(clientPackage, userId);
+                if (userId != mCurrentUserId) {
+                    final File systemDir = Environment.getUserSystemDirectory(userId);
+                    final File faceDir = new File(systemDir, FACE_DATA_DIR);
+                    if (!faceDir.exists()) {
+                        if (!faceDir.mkdir()) {
+                            Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
+                            return;
+                        }
+                        // Calling mkdir() from this process will create a directory with our
+                        // permissions (inherited from the containing dir). This command fixes
+                        // the label.
+                        if (!SELinux.restorecon(faceDir)) {
+                            Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+                            return;
+                        }
+                    }
+                    daemon.setActiveUser(userId, faceDir.getAbsolutePath());
+                    mCurrentUserId = userId;
+                }
+                mAuthenticatorIds.put(userId,
+                        hasEnrolledFace(userId) ? daemon.getAuthenticatorId().value : 0L);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to setActiveUser():", e);
+            }
+        }
+    }
+
+    /**
+     * @param clientPackage the package of the caller
+     * @return the profile id
+     */
+    private int getUserOrWorkProfileId(String clientPackage, int userId) {
+        if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+            return userId;
+        }
+        return getEffectiveUserId(userId);
+    }
+
+    /**
+     * @param userId
+     * @return true if this is a work profile
+     */
+    private boolean isWorkProfile(int userId) {
+        UserInfo userInfo = null;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            userInfo = mUserManager.getUserInfo(userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return userInfo != null && userInfo.isManagedProfile();
+    }
+
+    private void listenForUserSwitches() {
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(
+                new SynchronousUserSwitchObserver() {
+                    @Override
+                    public void onUserSwitching(int newUserId) throws RemoteException {
+                        mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+                                .sendToTarget();
+                    }
+                }, TAG);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to listen for user switching event" ,e);
+        }
+    }
+
+    /***
+     * @param opPackageName the name of the calling package
+     * @return authenticator id for the calling user
+     */
+    public long getAuthenticatorId(String opPackageName) {
+        final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
+        return mAuthenticatorIds.getOrDefault(userId, 0L);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
new file mode 100644
index 0000000..45b842d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.face.Face;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+
+/**
+ * Class managing the set of faces per user across device reboots.
+ */
+class FaceUserState {
+
+    private static final String TAG = "FaceState";
+    private static final String FACE_FILE = "settings_face.xml";
+
+    private static final String TAG_FACE = "face";
+    private static final String ATTR_DEVICE_ID = "deviceId";
+
+    private final File mFile;
+
+    @GuardedBy("this")
+    private Face mFace = null;
+    private final Context mCtx;
+
+    public FaceUserState(Context ctx, int userId) {
+        mFile = getFileForUser(userId);
+        mCtx = ctx;
+        synchronized (this) {
+            readStateSyncLocked();
+        }
+    }
+
+    public void addFace(int faceId) {
+        synchronized (this) {
+            mFace = new Face("Face", faceId, 0);
+            scheduleWriteStateLocked();
+        }
+    }
+
+    public void removeFace() {
+        synchronized (this) {
+            mFace = null;
+            scheduleWriteStateLocked();
+        }
+    }
+
+    public Face getFace() {
+        synchronized (this) {
+            return getCopy(mFace);
+        }
+    }
+
+    private static File getFileForUser(int userId) {
+        return new File(Environment.getUserSystemDirectory(userId), FACE_FILE);
+    }
+
+    private final Runnable mWriteStateRunnable = new Runnable() {
+        @Override
+        public void run() {
+            doWriteState();
+        }
+    };
+
+    private void scheduleWriteStateLocked() {
+        AsyncTask.execute(mWriteStateRunnable);
+    }
+
+    private Face getCopy(Face f) {
+        if (f == null) {
+            return null;
+        }
+        return new Face(f.getName(), f.getFaceId(), f.getDeviceId());
+    }
+
+    private void doWriteState() {
+        AtomicFile destination = new AtomicFile(mFile);
+
+        Face face;
+
+        synchronized (this) {
+            face = getCopy(mFace);
+        }
+
+        FileOutputStream out = null;
+        try {
+            out = destination.startWrite();
+
+            XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(out, "utf-8");
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_FACE);
+            if (face != null) {
+                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(face.getDeviceId()));
+            }
+            serializer.endTag(null, TAG_FACE);
+            serializer.endDocument();
+            destination.finishWrite(out);
+
+            // Any error while writing is fatal.
+        } catch (Throwable t) {
+            Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+            destination.failWrite(out);
+            throw new IllegalStateException("Failed to write face", t);
+        } finally {
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    private void readStateSyncLocked() {
+        FileInputStream in;
+        if (!mFile.exists()) {
+            return;
+        }
+        try {
+            in = new FileInputStream(mFile);
+        } catch (FileNotFoundException fnfe) {
+            Slog.i(TAG, "No face state");
+            return;
+        }
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, null);
+            parseStateLocked(parser);
+
+        } catch (XmlPullParserException | IOException e) {
+            throw new IllegalStateException("Failed parsing settings file: "
+                    + mFile , e);
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    private void parseStateLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_FACE)) {
+                parseFaceLocked(parser);
+            }
+        }
+    }
+
+    private void parseFaceLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+
+        mFace = new Face("", 0, Integer.parseInt(deviceId));
+    }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
new file mode 100644
index 0000000..c4a8499
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.face.Face;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Utility class for dealing with faces and face settings.
+ */
+public class FaceUtils {
+
+    private static final Object sInstanceLock = new Object();
+    private static FaceUtils sInstance;
+
+    @GuardedBy("this")
+    private final SparseArray<FaceUserState> mUsers = new SparseArray<>();
+
+    public static FaceUtils getInstance() {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new FaceUtils();
+            }
+        }
+        return sInstance;
+    }
+
+    private FaceUtils() {
+    }
+
+    public Face getFaceForUser(Context ctx, int userId) {
+        return getStateForUser(ctx, userId).getFace();
+    }
+
+    public void addFaceForUser(Context ctx, int faceId, int userId) {
+        getStateForUser(ctx, userId).addFace(faceId);
+    }
+
+    public void removeFaceForUser(Context ctx, int userId) {
+        getStateForUser(ctx, userId).removeFace();
+    }
+
+    private FaceUserState getStateForUser(Context ctx, int userId) {
+        synchronized (this) {
+            FaceUserState state = mUsers.get(userId);
+            if (state == null) {
+                state = new FaceUserState(ctx, userId);
+                mUsers.put(userId, state);
+            }
+            return state;
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/biometrics/face/InternalRemovalClient.java b/services/core/java/com/android/server/biometrics/face/InternalRemovalClient.java
new file mode 100644
index 0000000..634bf8b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/InternalRemovalClient.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.hardware.face.IFaceServiceReceiver;
+import com.android.server.biometrics.face.RemovalClient;
+
+public abstract class InternalRemovalClient extends RemovalClient {
+
+    public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
+            IFaceServiceReceiver receiver, int userId, boolean restricted, String owner) {
+
+        super(context, halDeviceId, token, receiver, userId, restricted, owner);
+
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/RemovalClient.java b/services/core/java/com/android/server/biometrics/face/RemovalClient.java
new file mode 100644
index 0000000..ccea9d5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/RemovalClient.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (C) 2018 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+
+    public RemovalClient(Context context, long halDeviceId, IBinder token,
+            IFaceServiceReceiver receiver, int userId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, restricted, owner);
+    }
+
+    @Override
+    public int start() {
+        IBiometricsFace daemon = getFaceDaemon();
+        // The face template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = daemon.remove(getTargetUserId());
+            if (result != 0) {
+                Slog.w(TAG, "startRemove failed, result=" + result);
+                MetricsLogger.histogram(getContext(), "faced_remove_start_error", result);
+                onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startRemove failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        if (mAlreadyCancelled) {
+            Slog.w(TAG, "stopRemove: already cancelled!");
+            return 0;
+        }
+        IBiometricsFace daemon = getFaceDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopRemoval: no face HAL!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancel();
+            if (result != 0) {
+                Slog.w(TAG, "stopRemoval failed, result=" + result);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopRemoval failed", e);
+            return ERROR_ESRCH;
+        }
+        mAlreadyCancelled = true;
+        return 0; // success
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendRemoved(int faceId, int remaining) {
+        IFaceServiceReceiver receiver = getReceiver();
+        try {
+            if (receiver != null) {
+                receiver.onRemoved(getHalDeviceId(), faceId, remaining);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify Removed:", e);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onRemoved(int faceId, int remaining) {
+        FaceUtils.getInstance().removeFaceForUser(getContext(), getTargetUserId());
+        return sendRemoved(faceId, remaining);
+    }
+
+    @Override
+    public boolean onEnrollResult(int faceId, int remaining) {
+        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
+        return true; // Invalid for Remove
+    }
+
+    @Override
+    public boolean onAuthenticated(int faceId) {
+        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
+        return true; // Invalid for Remove.
+    }
+}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 11f0701..811dc75 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -30,10 +30,6 @@
 import android.content.SyncRequest;
 import android.content.SyncStatusInfo;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteQueryBuilder;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -510,10 +506,6 @@
         readAccountInfoLocked();
         readStatusLocked();
         readStatisticsLocked();
-        readAndDeleteLegacyAccountInfoLocked();
-        writeAccountInfoLocked();
-        writeStatusLocked();
-        writeStatisticsLocked();
 
         if (mLogger.enabled()) {
             final int size = mAuthorities.size();
@@ -1581,7 +1573,6 @@
             readAccountInfoLocked();
             readStatusLocked();
             readStatisticsLocked();
-            readAndDeleteLegacyAccountInfoLocked();
             writeAccountInfoLocked();
             writeStatusLocked();
             writeStatisticsLocked();
@@ -1998,146 +1989,6 @@
         }
     }
 
-    static int getIntColumn(Cursor c, String name) {
-        return c.getInt(c.getColumnIndex(name));
-    }
-
-    static long getLongColumn(Cursor c, String name) {
-        return c.getLong(c.getColumnIndex(name));
-    }
-
-    /**
-     * TODO Remove it. It's super old code that was used to migrate the information from a sqlite
-     * database that we used a long time ago, and is no longer relevant.
-     */
-    private void readAndDeleteLegacyAccountInfoLocked() {
-        // Look for old database to initialize from.
-        File file = mContext.getDatabasePath("syncmanager.db");
-        if (!file.exists()) {
-            return;
-        }
-        String path = file.getPath();
-        SQLiteDatabase db = null;
-        try {
-            db = SQLiteDatabase.openDatabase(path, null,
-                    SQLiteDatabase.OPEN_READONLY);
-        } catch (SQLiteException e) {
-        }
-
-        if (db != null) {
-            final boolean hasType = db.getVersion() >= 11;
-
-            // Copy in all of the status information, as well as accounts.
-            if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
-                Slog.v(TAG_FILE, "Reading legacy sync accounts db");
-            }
-            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-            qb.setTables("stats, status");
-            HashMap<String,String> map = new HashMap<String,String>();
-            map.put("_id", "status._id as _id");
-            map.put("account", "stats.account as account");
-            if (hasType) {
-                map.put("account_type", "stats.account_type as account_type");
-            }
-            map.put("authority", "stats.authority as authority");
-            map.put("totalElapsedTime", "totalElapsedTime");
-            map.put("numSyncs", "numSyncs");
-            map.put("numSourceLocal", "numSourceLocal");
-            map.put("numSourcePoll", "numSourcePoll");
-            map.put("numSourceServer", "numSourceServer");
-            map.put("numSourceUser", "numSourceUser");
-            map.put("lastSuccessSource", "lastSuccessSource");
-            map.put("lastSuccessTime", "lastSuccessTime");
-            map.put("lastFailureSource", "lastFailureSource");
-            map.put("lastFailureTime", "lastFailureTime");
-            map.put("lastFailureMesg", "lastFailureMesg");
-            map.put("pending", "pending");
-            qb.setProjectionMap(map);
-            qb.appendWhere("stats._id = status.stats_id");
-            Cursor c = qb.query(db, null, null, null, null, null, null);
-            while (c.moveToNext()) {
-                String accountName = c.getString(c.getColumnIndex("account"));
-                String accountType = hasType
-                        ? c.getString(c.getColumnIndex("account_type")) : null;
-                if (accountType == null) {
-                    accountType = "com.google";
-                }
-                String authorityName = c.getString(c.getColumnIndex("authority"));
-                AuthorityInfo authority =
-                        this.getOrCreateAuthorityLocked(
-                                new EndPoint(new Account(accountName, accountType),
-                                        authorityName,
-                                        0 /* legacy is single-user */)
-                                , -1,
-                                false);
-                if (authority != null) {
-                    int i = mSyncStatus.size();
-                    boolean found = false;
-                    SyncStatusInfo st = null;
-                    while (i > 0) {
-                        i--;
-                        st = mSyncStatus.valueAt(i);
-                        if (st.authorityId == authority.ident) {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (!found) {
-                        st = new SyncStatusInfo(authority.ident);
-                        mSyncStatus.put(authority.ident, st);
-                    }
-                    st.totalStats.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
-                    st.totalStats.numSyncs = getIntColumn(c, "numSyncs");
-                    st.totalStats.numSourceLocal = getIntColumn(c, "numSourceLocal");
-                    st.totalStats.numSourcePoll = getIntColumn(c, "numSourcePoll");
-                    st.totalStats.numSourceOther = getIntColumn(c, "numSourceServer");
-                    st.totalStats.numSourceUser = getIntColumn(c, "numSourceUser");
-                    st.totalStats.numSourcePeriodic = 0;
-                    st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
-                    st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
-                    st.lastFailureSource = getIntColumn(c, "lastFailureSource");
-                    st.lastFailureTime = getLongColumn(c, "lastFailureTime");
-                    st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
-                    st.pending = getIntColumn(c, "pending") != 0;
-                }
-            }
-
-            c.close();
-
-            // Retrieve the settings.
-            qb = new SQLiteQueryBuilder();
-            qb.setTables("settings");
-            c = qb.query(db, null, null, null, null, null, null);
-            while (c.moveToNext()) {
-                String name = c.getString(c.getColumnIndex("name"));
-                String value = c.getString(c.getColumnIndex("value"));
-                if (name == null) continue;
-                if (name.equals("listen_for_tickles")) {
-                    setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0,
-                            ContentResolver.SYNC_EXEMPTION_NONE, SyncLogger.CALLING_UID_SELF);
-                } else if (name.startsWith("sync_provider_")) {
-                    String provider = name.substring("sync_provider_".length(),
-                            name.length());
-                    int i = mAuthorities.size();
-                    while (i > 0) {
-                        i--;
-                        AuthorityInfo authority = mAuthorities.valueAt(i);
-                        if (authority.target.provider.equals(provider)) {
-                            authority.enabled = value == null || Boolean.parseBoolean(value);
-                            authority.syncable = 1;
-                        }
-                    }
-                }
-            }
-
-            c.close();
-
-            db.close();
-
-            (new File(path)).delete();
-        }
-    }
-
     public static final int STATUS_FILE_END = 0;
     public static final int STATUS_FILE_ITEM = 100;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2c78303..9a15623 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -94,7 +94,6 @@
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -449,10 +448,6 @@
     private static final int SHELL_UID = Process.SHELL_UID;
     private static final int SE_UID = Process.SE_UID;
 
-    // Suffix used during package installation when copying/moving
-    // package apks to install directory.
-    private static final String INSTALL_PACKAGE_SUFFIX = "-";
-
     static final int SCAN_NO_DEX = 1<<0;
     static final int SCAN_UPDATE_SIGNATURE = 1<<1;
     static final int SCAN_NEW_INSTALL = 1<<2;
@@ -690,11 +685,7 @@
     // as the lock for the global state.  Methods that must be called with
     // this lock held have the prefix "LP".
     @GuardedBy("mPackages")
-    final ArrayMap<String, PackageParser.Package> mPackages =
-            new ArrayMap<String, PackageParser.Package>();
-
-    final ArrayMap<String, Set<String>> mKnownCodebase =
-            new ArrayMap<String, Set<String>>();
+    final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
 
     // Keys are isolated uids and values are the uid of the application
     // that created the isolated proccess.
@@ -793,18 +784,15 @@
             for (PackageParser.Package p : allPackages) {
                 if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
                     if (overlayPackages == null) {
-                        overlayPackages = new ArrayList<PackageParser.Package>();
+                        overlayPackages = new ArrayList<>();
                     }
                     overlayPackages.add(p);
                 }
             }
             if (overlayPackages != null) {
-                Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
-                    public int compare(PackageParser.Package p1, PackageParser.Package p2) {
-                        return p1.mOverlayPriority - p2.mOverlayPriority;
-                    }
-                };
-                Collections.sort(overlayPackages, cmp);
+                Comparator<PackageParser.Package> cmp =
+                        Comparator.comparingInt(p -> p.mOverlayPriority);
+                overlayPackages.sort(cmp);
             }
             return overlayPackages;
         }
@@ -818,7 +806,7 @@
             for (PackageParser.Package overlayPackage : overlayPackages) {
                 if (targetPath == null) {
                     if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<String>();
+                        overlayPathList = new ArrayList<>();
                     }
                     overlayPathList.add(overlayPackage.baseCodePath);
                     continue;
@@ -834,7 +822,7 @@
                             UserHandle.getSharedAppGid(
                                     UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
                     if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<String>();
+                        overlayPathList = new ArrayList<>();
                     }
                     overlayPathList.add(overlayPackage.baseCodePath);
                 } catch (InstallerException e) {
@@ -876,7 +864,7 @@
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mOverlayIsStatic) {
                         if (mOverlayPackages == null) {
-                            mOverlayPackages = new ArrayList<PackageParser.Package>();
+                            mOverlayPackages = new ArrayList<>();
                         }
                         mOverlayPackages.add(p);
                     }
@@ -937,24 +925,22 @@
 
     // Mapping from provider base names (first directory in content URI codePath)
     // to the provider information.
-    final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority =
-            new ArrayMap<String, PackageParser.Provider>();
+    final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = new ArrayMap<>();
 
     // Mapping from instrumentation class names to info about them.
     final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
-            new ArrayMap<ComponentName, PackageParser.Instrumentation>();
+            new ArrayMap<>();
 
     // Packages whose data we have transfered into another package, thus
     // should no longer exist.
-    final ArraySet<String> mTransferedPackages = new ArraySet<String>();
+    final ArraySet<String> mTransferedPackages = new ArraySet<>();
 
     // Broadcast actions that are only available to the system.
     @GuardedBy("mProtectedBroadcasts")
     final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
 
     /** List of packages waiting for verification. */
-    final SparseArray<PackageVerificationState> mPendingVerification
-            = new SparseArray<PackageVerificationState>();
+    final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
 
     final PackageInstallerService mInstallerService;
 
@@ -1005,7 +991,7 @@
     final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
 
     final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
-            = new SparseArray<IntentFilterVerificationState>();
+            = new SparseArray<>();
 
     // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
@@ -1035,7 +1021,6 @@
             pkg = _pkg;
             replacing = _replacing;
             userId = _userId;
-            replacing = _replacing;
             verifierUid = _verifierUid;
         }
     }
@@ -1050,7 +1035,7 @@
     private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
         private Context mContext;
         private ComponentName mIntentFilterVerifierComponent;
-        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
 
         public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
             mContext = context;
@@ -1140,7 +1125,7 @@
             mIntentFilterVerificationStates.remove(verificationId);
 
             final String packageName = ivs.getPackageName();
-            IntentFilterVerificationInfo ivi = null;
+            IntentFilterVerificationInfo ivi;
 
             synchronized (mPackages) {
                 ivi = mSettings.getIntentFilterVerificationLPr(packageName);
@@ -1245,7 +1230,7 @@
         final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
 
         public PendingPackageBroadcasts() {
-            mUidMap = new SparseArray<ArrayMap<String, ArrayList<String>>>(2);
+            mUidMap = new SparseArray<>(2);
         }
 
         public ArrayList<String> get(int userId, String packageName) {
@@ -1297,7 +1282,7 @@
         private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
             ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
             if (map == null) {
-                map = new ArrayMap<String, ArrayList<String>>();
+                map = new ArrayMap<>();
                 mUidMap.put(userId, map);
             }
             return map;
@@ -1312,11 +1297,9 @@
 
     static final int SEND_PENDING_BROADCAST = 1;
     static final int MCS_BOUND = 3;
-    static final int END_COPY = 4;
     static final int INIT_COPY = 5;
     static final int MCS_UNBIND = 6;
     static final int START_CLEANING_PACKAGE = 7;
-    static final int FIND_INSTALL_LOC = 8;
     static final int POST_INSTALL = 9;
     static final int MCS_RECONNECT = 10;
     static final int MCS_GIVE_UP = 11;
@@ -1341,7 +1324,7 @@
     static UserManagerService sUserManager;
 
     // Stores a list of users whose package restrictions file needs to be updated
-    private ArraySet<Integer> mDirtyUsers = new ArraySet<Integer>();
+    private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
 
     final private DefaultContainerConnection mDefContainerConn =
             new DefaultContainerConnection();
@@ -1370,7 +1353,7 @@
         }
     }
 
-    final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
+    final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
     int mNextInstallToken = 1;  // nonzero; will be wrapped back to 1 when ++ overflows
 
     // XML tags for backup/restore of various bits of state
@@ -1417,7 +1400,7 @@
     class PackageHandler extends Handler {
         private boolean mBound = false;
         final ArrayList<HandlerParams> mPendingInstalls =
-            new ArrayList<HandlerParams>();
+                new ArrayList<>();
 
         private boolean connectToService() {
             if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
@@ -1623,9 +1606,6 @@
                     int uids[];
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                     synchronized (mPackages) {
-                        if (mPendingBroadcasts == null) {
-                            return;
-                        }
                         size = mPendingBroadcasts.size();
                         if (size <= 0) {
                             // Nothing to be done. Just return
@@ -1900,12 +1880,7 @@
     private PermissionCallback mPermissionCallback = new PermissionCallback() {
         @Override
         public void onGidsChanged(int appId, int userId) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
-                }
-            });
+            mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
         }
         @Override
         public void onPermissionGranted(int uid, int userId) {
@@ -2545,13 +2520,11 @@
                 requestCopyPreoptedFiles();
             }
 
-            String customResolverActivity = Resources.getSystem().getString(
+            String customResolverActivityName = Resources.getSystem().getString(
                     R.string.config_customResolverActivity);
-            if (TextUtils.isEmpty(customResolverActivity)) {
-                customResolverActivity = null;
-            } else {
+            if (!TextUtils.isEmpty(customResolverActivityName)) {
                 mCustomResolverComponentName = ComponentName.unflattenFromString(
-                        customResolverActivity);
+                        customResolverActivityName);
             }
 
             long startTime = SystemClock.uptimeMillis();
@@ -3401,7 +3374,7 @@
             }
         }
         if (ret != PackageManager.INSTALL_SUCCEEDED) {
-            if (dstCodePath == null || !dstCodePath.exists()) {
+            if (!dstCodePath.exists()) {
                 return null;
             }
             removeCodePathLI(dstCodePath);
@@ -3752,7 +3725,7 @@
                     for (ActivityIntentInfo filter : a.intents) {
                         if (hasValidDomains(filter)) {
                             if (domains == null) {
-                                domains = new ArraySet<String>();
+                                domains = new ArraySet<>();
                             }
                             domains.addAll(filter.getHostsList());
                         }
@@ -3819,7 +3792,7 @@
                 PackageManager.MATCH_ALL, userId);
 
         final int count = list.size();
-        List<String> result = new ArrayList<String>(count);
+        List<String> result = new ArrayList<>(count);
         for (int i=0; i<count; i++) {
             ResolveInfo info = list.get(i);
             if (info.activityInfo == null
@@ -3874,16 +3847,6 @@
         }
     }
 
-    static int[] appendInts(int[] cur, int[] add) {
-        if (add == null) return cur;
-        if (cur == null) return add;
-        final int N = add.length;
-        for (int i=0; i<N; i++) {
-            cur = appendInt(cur, add[i]);
-        }
-        return cur;
-    }
-
     /**
      * Returns whether or not a full application can see an instant application.
      * <p>
@@ -3947,7 +3910,7 @@
                     ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
             // Compute granted permissions only if package has requested permissions
             final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
-                    ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
+                    ? Collections.emptySet() : permissionsState.getPermissions(userId);
 
             PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
                     ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
@@ -5763,7 +5726,7 @@
         final int callingUserId = UserHandle.getUserId(callingUid);
         synchronized (mPackages) {
             if (canViewInstantApps(callingUid, callingUserId)) {
-                return new ArrayList<String>(mPackages.keySet());
+                return new ArrayList<>(mPackages.keySet());
             }
             final String instantAppPkgName = getInstantAppPackageName(callingUid);
             final List<String> result = new ArrayList<>();
@@ -6635,7 +6598,7 @@
         flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
                 comp != null || pkgName != null /*onlyExposedExplicitly*/);
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags, userId);
             if (ai != null) {
                 // When specifying an explicit component, we prevent the activity from being
@@ -6691,7 +6654,7 @@
                 ResolveInfo xpResolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,
                         resolvedType, flags, userId);
                 if (xpResolveInfo != null) {
-                    List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+                    List<ResolveInfo> xpResult = new ArrayList<>(1);
                     xpResult.add(xpResolveInfo);
                     return applyPostResolutionFilter(
                             filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
@@ -7116,12 +7079,12 @@
                     candidates.size());
         }
 
-        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> alwaysList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> undefinedList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> alwaysAskList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> neverList = new ArrayList<ResolveInfo>();
-        ArrayList<ResolveInfo> matchAllList = new ArrayList<ResolveInfo>();
+        final ArrayList<ResolveInfo> result = new ArrayList<>();
+        final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
+        final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+        final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
+        final ArrayList<ResolveInfo> neverList = new ArrayList<>();
+        final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
 
         synchronized (mPackages) {
             final int count = candidates.size();
@@ -7596,7 +7559,7 @@
             }
         }
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
             if (ai != null) {
                 // When specifying an explicit component, we prevent the activity from being
@@ -7710,7 +7673,7 @@
             }
         }
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ServiceInfo si = getServiceInfo(comp, flags, userId);
             if (si != null) {
                 // When specifying an explicit component, we prevent the service from being
@@ -7828,7 +7791,7 @@
             }
         }
         if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+            final List<ResolveInfo> list = new ArrayList<>(1);
             final ProviderInfo pi = getProviderInfo(comp, flags, userId);
             if (pi != null) {
                 // When specifying an explicit component, we prevent the provider from being
@@ -8027,7 +7990,7 @@
 
         // writer
         synchronized (mPackages) {
-            ArrayList<PackageInfo> list = new ArrayList<PackageInfo>();
+            ArrayList<PackageInfo> list = new ArrayList<>();
             boolean[] tmpBools = new boolean[permissions.length];
             if (listUninstalled) {
                 for (PackageSetting ps : mSettings.mPackages.values()) {
@@ -8044,7 +8007,7 @@
                 }
             }
 
-            return new ParceledListSlice<PackageInfo>(list);
+            return new ParceledListSlice<>(list);
         }
     }
 
@@ -8245,7 +8208,7 @@
     }
 
     private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
-        final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+        final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
 
         // reader
         synchronized (mPackages) {
@@ -8374,7 +8337,7 @@
                         continue;
                     }
                     if (finalList == null) {
-                        finalList = new ArrayList<ProviderInfo>(3);
+                        finalList = new ArrayList<>(3);
                     }
                     ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
                             ps.readUserState(userId), userId);
@@ -8386,8 +8349,8 @@
         }
 
         if (finalList != null) {
-            Collections.sort(finalList, mProviderInitOrderSorter);
-            return new ParceledListSlice<ProviderInfo>(finalList);
+            finalList.sort(mProviderInitOrderSorter);
+            return new ParceledListSlice<>(finalList);
         }
 
         return ParceledListSlice.emptyList();
@@ -8423,7 +8386,7 @@
 
     private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
             int flags) {
-        ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>();
+        ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
 
         // reader
         synchronized (mPackages) {
@@ -8493,10 +8456,8 @@
                         renameStaticSharedLibraryPackage(parseResult.pkg);
                     }
                     try {
-                        if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
-                            scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
-                                    currentTime, null);
-                        }
+                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
+                                currentTime, null);
                     } catch (PackageManagerException e) {
                         errorCode = e.error;
                         Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
@@ -8698,7 +8659,7 @@
      * match one in a trusted source, and should be done separately.
      */
     private boolean canSkipFullApkVerification(String apkPath) {
-        byte[] rootHashObserved = null;
+        final byte[] rootHashObserved;
         try {
             rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
             if (rootHashObserved == null) {
@@ -8964,7 +8925,7 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private static final void enforceSystemOrRoot(String message) {
+    private static void enforceSystemOrRoot(String message) {
         final int uid = Binder.getCallingUid();
         if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
             throw new SecurityException(message);
@@ -9367,7 +9328,7 @@
     }
 
     public ArraySet<String> getOptimizablePackages() {
-        ArraySet<String> pkgs = new ArraySet<String>();
+        ArraySet<String> pkgs = new ArraySet<>();
         synchronized (mPackages) {
             for (PackageParser.Package p : mPackages.values()) {
                 if (PackageDexOptimizer.canOptimizePackage(p)) {
@@ -12943,9 +12904,7 @@
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
-            if (info != null) {
-                res.handleAllWebDataURI = info.handleAllWebDataURI();
-            }
+            res.handleAllWebDataURI = info.handleAllWebDataURI();
             res.priority = info.getPriority();
             res.preferredOrder = activity.owner.mPreferredOrder;
             //System.out.println("Result: " + res.activityInfo.className +
@@ -12967,7 +12926,7 @@
 
         @Override
         protected void sortResults(List<ResolveInfo> results) {
-            Collections.sort(results, mResolvePrioritySorter);
+            results.sort(mResolvePrioritySorter);
         }
 
         @Override
@@ -13000,7 +12959,7 @@
 
         // Keys are String (activity class name), values are Activity.
         private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
-                = new ArrayMap<ComponentName, PackageParser.Activity>();
+                = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -13030,8 +12989,7 @@
             mFlags = flags;
             final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int N = packageServices.size();
-            ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
-                new ArrayList<PackageParser.ServiceIntentInfo[]>(N);
+            ArrayList<PackageParser.ServiceIntentInfo[]> listCut = new ArrayList<>(N);
 
             ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
             for (int i = 0; i < N; ++i) {
@@ -13184,7 +13142,7 @@
 
         @Override
         protected void sortResults(List<ResolveInfo> results) {
-            Collections.sort(results, mResolvePrioritySorter);
+            results.sort(mResolvePrioritySorter);
         }
 
         @Override
@@ -13233,8 +13191,7 @@
 //        }
 
         // Keys are String (activity class name), values are Activity.
-        private final ArrayMap<ComponentName, PackageParser.Service> mServices
-                = new ArrayMap<ComponentName, PackageParser.Service>();
+        private final ArrayMap<ComponentName, PackageParser.Service> mServices = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -13266,8 +13223,7 @@
             mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int N = packageProviders.size();
-            ArrayList<PackageParser.ProviderIntentInfo[]> listCut =
-                    new ArrayList<PackageParser.ProviderIntentInfo[]>(N);
+            ArrayList<PackageParser.ProviderIntentInfo[]> listCut = new ArrayList<>(N);
 
             ArrayList<PackageParser.ProviderIntentInfo> intentFilters;
             for (int i = 0; i < N; ++i) {
@@ -13427,7 +13383,7 @@
 
         @Override
         protected void sortResults(List<ResolveInfo> results) {
-            Collections.sort(results, mResolvePrioritySorter);
+            results.sort(mResolvePrioritySorter);
         }
 
         @Override
@@ -13460,7 +13416,7 @@
         }
 
         private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
-                = new ArrayMap<ComponentName, PackageParser.Provider>();
+                = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -13545,78 +13501,69 @@
         }
     }
 
-    private static final Comparator<ResolveInfo> mResolvePrioritySorter =
-            new Comparator<ResolveInfo>() {
-        public int compare(ResolveInfo r1, ResolveInfo r2) {
-            int v1 = r1.priority;
-            int v2 = r2.priority;
-            //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
-            if (v1 != v2) {
-                return (v1 > v2) ? -1 : 1;
-            }
-            v1 = r1.preferredOrder;
-            v2 = r2.preferredOrder;
-            if (v1 != v2) {
-                return (v1 > v2) ? -1 : 1;
-            }
-            if (r1.isDefault != r2.isDefault) {
-                return r1.isDefault ? -1 : 1;
-            }
-            v1 = r1.match;
-            v2 = r2.match;
-            //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
-            if (v1 != v2) {
-                return (v1 > v2) ? -1 : 1;
-            }
-            if (r1.system != r2.system) {
-                return r1.system ? -1 : 1;
-            }
-            if (r1.activityInfo != null) {
-                return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
-            }
-            if (r1.serviceInfo != null) {
-                return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
-            }
-            if (r1.providerInfo != null) {
-                return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
-            }
-            return 0;
+    private static final Comparator<ResolveInfo> mResolvePrioritySorter = (r1, r2) -> {
+        int v1 = r1.priority;
+        int v2 = r2.priority;
+        //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
+        if (v1 != v2) {
+            return (v1 > v2) ? -1 : 1;
         }
+        v1 = r1.preferredOrder;
+        v2 = r2.preferredOrder;
+        if (v1 != v2) {
+            return (v1 > v2) ? -1 : 1;
+        }
+        if (r1.isDefault != r2.isDefault) {
+            return r1.isDefault ? -1 : 1;
+        }
+        v1 = r1.match;
+        v2 = r2.match;
+        //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
+        if (v1 != v2) {
+            return (v1 > v2) ? -1 : 1;
+        }
+        if (r1.system != r2.system) {
+            return r1.system ? -1 : 1;
+        }
+        if (r1.activityInfo != null) {
+            return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+        }
+        if (r1.serviceInfo != null) {
+            return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+        }
+        if (r1.providerInfo != null) {
+            return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+        }
+        return 0;
     };
 
-    private static final Comparator<ProviderInfo> mProviderInitOrderSorter =
-            new Comparator<ProviderInfo>() {
-        public int compare(ProviderInfo p1, ProviderInfo p2) {
-            final int v1 = p1.initOrder;
-            final int v2 = p2.initOrder;
-            return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
-        }
+    private static final Comparator<ProviderInfo> mProviderInitOrderSorter = (p1, p2) -> {
+        final int v1 = p1.initOrder;
+        final int v2 = p2.initOrder;
+        return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
     };
 
     @Override
     public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
             final int[] userIds, int[] instantUserIds) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    final IActivityManager am = ActivityManager.getService();
-                    if (am == null) return;
-                    final int[] resolvedUserIds;
-                    if (userIds == null) {
-                        resolvedUserIds = am.getRunningUserIds();
-                    } else {
-                        resolvedUserIds = userIds;
-                    }
-                    doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
-                            resolvedUserIds, false);
-                    if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
-                        doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
-                                instantUserIds, true);
-                    }
-                } catch (RemoteException ex) {
+        mHandler.post(() -> {
+            try {
+                final IActivityManager am = ActivityManager.getService();
+                if (am == null) return;
+                final int[] resolvedUserIds;
+                if (userIds == null) {
+                    resolvedUserIds = am.getRunningUserIds();
+                } else {
+                    resolvedUserIds = userIds;
                 }
+                doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+                        resolvedUserIds, false);
+                if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
+                    doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+                            instantUserIds, true);
+                }
+            } catch (RemoteException ex) {
             }
         });
     }
@@ -14283,25 +14230,22 @@
         } else {
             action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
         }
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    final IActivityManager am = ActivityManager.getService();
-                    if (am == null) {
-                        Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
-                                + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
-                        return;
-                    }
-                    final int[] targetUserIds = new int[] {userId};
-                    for (String packageName : affectedPackages) {
-                        doSendBroadcast(am, action, null, intentExtras,
-                                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
-                                targetUserIds, false);
-                    }
-                } catch (RemoteException ex) {
-                    // Shouldn't happen as AMS is in the same process.
+        mHandler.post(() -> {
+            try {
+                final IActivityManager am = ActivityManager.getService();
+                if (am == null) {
+                    Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+                            + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+                    return;
                 }
+                final int[] targetUserIds = new int[] {userId};
+                for (String packageName : affectedPackages) {
+                    doSendBroadcast(am, action, null, intentExtras,
+                            Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+                            targetUserIds, false);
+                }
+            } catch (RemoteException ex) {
+                // Shouldn't happen as AMS is in the same process.
             }
         });
     }
@@ -14549,7 +14493,7 @@
         }
 
         final int N = pkgInfo.verifiers.length;
-        final List<ComponentName> sufficientVerifiers = new ArrayList<ComponentName>(N + 1);
+        final List<ComponentName> sufficientVerifiers = new ArrayList<>(N + 1);
         for (int i = 0; i < N; i++) {
             final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
 
@@ -15059,36 +15003,33 @@
         // are coherent.  In the non-restore case, the app has already completed install
         // and been launched through some other means, so it is not in a problematic
         // state for observers to see the FIRST_LAUNCH signal.
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                for (int i = 0; i < mRunningInstalls.size(); i++) {
-                    final PostInstallData data = mRunningInstalls.valueAt(i);
-                    if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-                        continue;
-                    }
-                    if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
-                        // right package; but is it for the right user?
-                        for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
-                            if (userId == data.res.newUsers[uIndex]) {
-                                if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "Package " + packageName
-                                            + " being restored so deferring FIRST_LAUNCH");
-                                }
-                                return;
+        mHandler.post(() -> {
+            for (int i = 0; i < mRunningInstalls.size(); i++) {
+                final PostInstallData data = mRunningInstalls.valueAt(i);
+                if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+                    continue;
+                }
+                if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
+                    // right package; but is it for the right user?
+                    for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
+                        if (userId == data.res.newUsers[uIndex]) {
+                            if (DEBUG_BACKUP) {
+                                Slog.i(TAG, "Package " + packageName
+                                        + " being restored so deferring FIRST_LAUNCH");
                             }
+                            return;
                         }
                     }
                 }
-                // didn't find it, so not being restored
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
-                }
-                final boolean isInstantApp = isInstantApp(packageName, userId);
-                final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
-                final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
-                sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
             }
+            // didn't find it, so not being restored
+            if (DEBUG_BACKUP) {
+                Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
+            }
+            final boolean isInstantApp = isInstantApp(packageName, userId);
+            final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+            final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+            sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
         });
     }
 
@@ -15917,8 +15858,7 @@
                 }
             };
 
-            int ret = PackageManager.INSTALL_SUCCEEDED;
-            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
+            int ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
             if (ret != PackageManager.INSTALL_SUCCEEDED) {
                 Slog.e(TAG, "Failed to copy package");
                 return ret;
@@ -16063,17 +16003,6 @@
     }
 
     /**
-     * Extract the StorageManagerService "container ID" from the full code path of an
-     * .apk.
-     */
-    static String cidFromCodePath(String fullCodePath) {
-        int eidx = fullCodePath.lastIndexOf("/");
-        String subStr1 = fullCodePath.substring(0, eidx);
-        int sidx = subStr1.lastIndexOf("/");
-        return subStr1.substring(sidx+1, eidx);
-    }
-
-    /**
      * Logic to handle movement of existing installed applications.
      */
     class MoveInstallArgs extends InstallArgs {
@@ -16184,51 +16113,6 @@
         }
     }
 
-    static String getAsecPackageName(String packageCid) {
-        int idx = packageCid.lastIndexOf("-");
-        if (idx == -1) {
-            return packageCid;
-        }
-        return packageCid.substring(0, idx);
-    }
-
-    // Utility method used to create code paths based on package name and available index.
-    private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
-        String idxStr = "";
-        int idx = 1;
-        // Fall back to default value of idx=1 if prefix is not
-        // part of oldCodePath
-        if (oldCodePath != null) {
-            String subStr = oldCodePath;
-            // Drop the suffix right away
-            if (suffix != null && subStr.endsWith(suffix)) {
-                subStr = subStr.substring(0, subStr.length() - suffix.length());
-            }
-            // If oldCodePath already contains prefix find out the
-            // ending index to either increment or decrement.
-            int sidx = subStr.lastIndexOf(prefix);
-            if (sidx != -1) {
-                subStr = subStr.substring(sidx + prefix.length());
-                if (subStr != null) {
-                    if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) {
-                        subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length());
-                    }
-                    try {
-                        idx = Integer.parseInt(subStr);
-                        if (idx <= 1) {
-                            idx++;
-                        } else {
-                            idx--;
-                        }
-                    } catch(NumberFormatException e) {
-                    }
-                }
-            }
-        }
-        idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx);
-        return prefix + idxStr;
-    }
-
     private File getNextCodePath(File targetDir, String packageName) {
         File result;
         SecureRandom random = new SecureRandom();
@@ -16439,7 +16323,7 @@
 
             // don't allow a system upgrade unless the upgrade hash matches
             if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
-                byte[] digestBytes = null;
+                final byte[] digestBytes;
                 try {
                     final MessageDigest digest = MessageDigest.getInstance("SHA-512");
                     updateDigest(digest, new File(pkg.baseCodePath));
@@ -16564,18 +16448,10 @@
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
             // Set the system/privileged/oem/vendor/product flags as needed
-            final boolean privileged =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
-            final boolean oem =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
-            final boolean vendor =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
-            final boolean product =
-                    (oldPackage.applicationInfo.privateFlags
-                            & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+            final boolean privileged = isPrivilegedApp(oldPackage);
+            final boolean oem = isOemApp(oldPackage);
+            final boolean vendor = isVendorApp(oldPackage);
+            final boolean product = isProductApp(oldPackage);
             final @ParseFlags int systemParseFlags = parseFlags;
             final @ScanFlags int systemScanFlags = scanFlags
                     | SCAN_AS_SYSTEM
@@ -16626,7 +16502,7 @@
                     Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
                 }
                 final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
-                final ArrayList<String> pkgList = new ArrayList<String>(1);
+                final ArrayList<String> pkgList = new ArrayList<>(1);
                 pkgList.add(deletedPackage.applicationInfo.packageName);
                 sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
             }
@@ -17852,11 +17728,8 @@
     }
 
     private void deleteTempPackageFiles() {
-        final FilenameFilter filter = new FilenameFilter() {
-            public boolean accept(File dir, String name) {
-                return name.startsWith("vmdl") && name.endsWith(".tmp");
-            }
-        };
+        final FilenameFilter filter =
+                (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
         for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
             file.delete();
         }
@@ -18193,8 +18066,8 @@
             return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
         }
 
-        PackageSetting uninstalledPs = null;
-        PackageParser.Package pkg = null;
+        PackageSetting uninstalledPs;
+        PackageParser.Package pkg;
 
         // for the uninstall-updates case and restricted profiles, remember the per-
         // user handle installed state
@@ -18504,13 +18377,10 @@
                             if (userIdToKill == UserHandle.USER_ALL
                                     || userIdToKill >= UserHandle.USER_SYSTEM) {
                                 // If gids changed for this user, kill all affected packages.
-                                mHandler.post(new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        // This has to happen with no lock held.
-                                        killApplication(deletedPs.name, deletedPs.appId,
-                                                KILL_APP_REASON_GIDS_CHANGED);
-                                    }
+                                mHandler.post(() -> {
+                                    // This has to happen with no lock held.
+                                    killApplication(deletedPs.name, deletedPs.appId,
+                                            KILL_APP_REASON_GIDS_CHANGED);
                                 });
                                 break;
                             }
@@ -19452,12 +19322,8 @@
                     case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                         writeRuntimePermissions = true;
                         final int appId = ps.appId;
-                        mHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
-                            }
-                        });
+                        mHandler.post(
+                                () -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
                     } break;
                 }
             }
@@ -19535,33 +19401,31 @@
         }
 
         // Queue up an async operation since the package deletion may take a little while.
-        mHandler.post(new Runnable() {
-            public void run() {
-                final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
-                boolean doClearData = true;
-                if (ps != null) {
-                    final boolean targetIsInstantApp =
-                            ps.getInstantApp(UserHandle.getUserId(callingUid));
-                    doClearData = !targetIsInstantApp
-                            || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+        mHandler.post(() -> {
+            final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
+            boolean doClearData = true;
+            if (ps != null) {
+                final boolean targetIsInstantApp =
+                        ps.getInstantApp(UserHandle.getUserId(callingUid));
+                doClearData = !targetIsInstantApp
+                        || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+            }
+            if (doClearData) {
+                synchronized (mInstallLock) {
+                    final int flags = StorageManager.FLAG_STORAGE_DE
+                            | StorageManager.FLAG_STORAGE_CE;
+                    // We're only clearing cache files, so we don't care if the
+                    // app is unfrozen and still able to run
+                    clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+                    clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                 }
-                if (doClearData) {
-                    synchronized (mInstallLock) {
-                        final int flags = StorageManager.FLAG_STORAGE_DE
-                                | StorageManager.FLAG_STORAGE_CE;
-                        // We're only clearing cache files, so we don't care if the
-                        // app is unfrozen and still able to run
-                        clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
-                        clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-                    }
-                    clearExternalStorageDataSync(packageName, userId, false);
-                }
-                if (observer != null) {
-                    try {
-                        observer.onRemoveCompleted(packageName, true);
-                    } catch (RemoteException e) {
-                        Log.i(TAG, "Observer no longer exists.");
-                    }
+                clearExternalStorageDataSync(packageName, userId, false);
+            }
+            if (observer != null) {
+                try {
+                    observer.onRemoveCompleted(packageName, true);
+                } catch (RemoteException e) {
+                    Log.i(TAG, "Observer no longer exists.");
                 }
             }
         });
@@ -19844,7 +19708,7 @@
                         (pa.mPref.mComponent.getPackageName().equals(packageName)
                                 && pa.mPref.mAlways)) {
                     if (removed == null) {
-                        removed = new ArrayList<PreferredActivity>();
+                        removed = new ArrayList<>();
                     }
                     removed.add(pa);
                 }
@@ -20008,7 +19872,7 @@
                     // Mark entry for removal only if it matches the package name.
                     if (ppa.mComponent.getPackageName().equals(packageName)) {
                         if (removed == null) {
-                            removed = new ArrayList<PersistentPreferredActivity>();
+                            removed = new ArrayList<>();
                         }
                         removed.add(ppa);
                     }
@@ -20047,7 +19911,6 @@
             }
             return;
         }
-Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
         // this is supposed to be TAG_PREFERRED_BACKUP
         if (!expectedStartTag.equals(parser.getName())) {
             if (DEBUG_BACKUP) {
@@ -20058,12 +19921,11 @@
 
         // skip interfering stuff, then we're aligned with the backing implementation
         while ((type = parser.next()) == XmlPullParser.TEXT) { }
-Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
         functor.apply(parser, userId);
     }
 
     private interface BlobXmlRestorer {
-        public void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
+        void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
     }
 
     /**
@@ -20111,15 +19973,11 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                mSettings.readPreferredActivitiesLPw(parser, userId);
-                            }
+                    (readParser, readUserId) -> {
+                        synchronized (mPackages) {
+                            mSettings.readPreferredActivitiesLPw(readParser, readUserId);
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20172,15 +20030,11 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                mSettings.readDefaultAppsLPw(parser, userId);
-                            }
+                    (parser1, userId1) -> {
+                        synchronized (mPackages) {
+                            mSettings.readDefaultAppsLPw(parser1, userId1);
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
@@ -20228,16 +20082,12 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                mSettings.readAllDomainVerificationsLPr(parser, userId);
-                                mSettings.writeLPr();
-                            }
+                    (parser1, userId1) -> {
+                        synchronized (mPackages) {
+                            mSettings.readAllDomainVerificationsLPr(parser1, userId1);
+                            mSettings.writeLPr();
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20285,15 +20135,11 @@
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
-                    new BlobXmlRestorer() {
-                        @Override
-                        public void apply(XmlPullParser parser, int userId)
-                                throws XmlPullParserException, IOException {
-                            synchronized (mPackages) {
-                                processRestoredPermissionGrantsLPr(parser, userId);
-                            }
+                    (parser1, userId1) -> {
+                        synchronized (mPackages) {
+                            processRestoredPermissionGrantsLPr(parser1, userId1);
                         }
-                    } );
+                    });
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
                 Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20481,7 +20327,7 @@
             CrossProfileIntentResolver resolver =
                     mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
             ArraySet<CrossProfileIntentFilter> set =
-                    new ArraySet<CrossProfileIntentFilter>(resolver.filterSet());
+                    new ArraySet<>(resolver.filterSet());
             for (CrossProfileIntentFilter filter : set) {
                 if (filter.getOwnerPackage().equals(ownerPackage)) {
                     resolver.removeFilter(filter);
@@ -20586,9 +20432,7 @@
 
         allHomeCandidates.clear();
         if (list != null) {
-            for (ResolveInfo ri : list) {
-                allHomeCandidates.add(ri);
-            }
+            allHomeCandidates.addAll(list);
         }
         return (preferred == null || preferred.activityInfo == null)
                 ? null
@@ -20718,7 +20562,6 @@
         boolean isApp = (className == null);
         final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
         String componentName = isApp ? packageName : className;
-        int packageUid = -1;
         ArrayList<String> components;
 
         // reader
@@ -20958,7 +20801,7 @@
             components = mPendingBroadcasts.get(userId, packageName);
             final boolean newPackage = components == null;
             if (newPackage) {
-                components = new ArrayList<String>();
+                components = new ArrayList<>();
             }
             if (!components.contains(componentName)) {
                 components.add(componentName);
@@ -20982,7 +20825,7 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             if (sendNow) {
-                packageUid = UserHandle.getUid(userId, pkgSetting.appId);
+                int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
                 sendPackageChangedBroadcast(packageName,
                         (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
             }
@@ -21165,7 +21008,7 @@
             // possible for the user flow to never be able to return to that
             // situation so here we do a sanity check to make sure we haven't
             // left any junk around.
-            ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
+            ArrayList<PreferredActivity> removed = new ArrayList<>();
             for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
                 PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
                 removed.clear();
@@ -21293,16 +21136,16 @@
     }
 
     static String arrayToString(int[] array) {
-        StringBuffer buf = new StringBuffer(128);
-        buf.append('[');
+        StringBuilder stringBuilder = new StringBuilder(128);
+        stringBuilder.append('[');
         if (array != null) {
             for (int i=0; i<array.length; i++) {
-                if (i > 0) buf.append(", ");
-                buf.append(array[i]);
+                if (i > 0) stringBuilder.append(", ");
+                stringBuilder.append(array[i]);
             }
         }
-        buf.append(']');
-        return buf.toString();
+        stringBuilder.append(']');
+        return stringBuilder.toString();
     }
 
     @Override
@@ -22013,7 +21856,7 @@
         ipw.println();
         ipw.println("Dexopt state:");
         ipw.increaseIndent();
-        Collection<PackageParser.Package> packages = null;
+        Collection<PackageParser.Package> packages;
         if (packageName != null) {
             PackageParser.Package targetPackage = mPackages.get(packageName);
             if (targetPackage != null) {
@@ -22040,7 +21883,7 @@
         ipw.println();
         ipw.println("Compiler stats:");
         ipw.increaseIndent();
-        Collection<PackageParser.Package> packages = null;
+        Collection<PackageParser.Package> packages;
         if (packageName != null) {
             PackageParser.Package targetPackage = mPackages.get(packageName);
             if (targetPackage != null) {
@@ -22075,9 +21918,7 @@
         ArraySet<String> result = new ArraySet<>();
         if (iviList.size() > 0) {
             for (IntentFilterVerificationInfo ivi : iviList) {
-                for (String host : ivi.getDomains()) {
-                    result.add(host);
-                }
+                result.addAll(ivi.getDomains());
             }
         }
         if (filters != null && filters.size() > 0) {
@@ -22169,12 +22010,7 @@
     }
 
     private void loadPrivatePackages(final VolumeInfo vol) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                loadPrivatePackagesInner(vol);
-            }
-        });
+        mHandler.post(() -> loadPrivatePackagesInner(vol));
     }
 
     private void loadPrivatePackagesInner(VolumeInfo vol) {
@@ -22265,12 +22101,7 @@
     }
 
     private void unloadPrivatePackages(final VolumeInfo vol) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                unloadPrivatePackagesInner(vol);
-            }
-        });
+        mHandler.post(() -> unloadPrivatePackagesInner(vol));
     }
 
     private void unloadPrivatePackagesInner(VolumeInfo vol) {
@@ -22325,23 +22156,6 @@
         }
     }
 
-    private void assertPackageKnown(String volumeUuid, String packageName)
-            throws PackageManagerException {
-        synchronized (mPackages) {
-            // Normalize package name to handle renamed packages
-            packageName = normalizePackageNameLPr(packageName);
-
-            final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                throw new PackageManagerException("Package " + packageName + " is unknown");
-            } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
-                throw new PackageManagerException(
-                        "Package " + packageName + " found on unknown volume " + volumeUuid
-                                + "; expected volume " + ps.volumeUuid);
-            }
-        }
-    }
-
     private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
             throws PackageManagerException {
         synchronized (mPackages) {
@@ -22454,7 +22268,7 @@
      * <p>
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps.
-     * @returns list of skipped non-core packages (if {@code onlyCoreApps} is true)
+     * @return list of skipped non-core packages (if {@code onlyCoreApps} is true)
      */
     private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
             boolean migrateAppData, boolean onlyCoreApps) {
@@ -22824,10 +22638,7 @@
         @Override
         protected void finalize() throws Throwable {
             try {
-                if (mCloseGuard != null) {
-                    mCloseGuard.warnIfOpen();
-                }
-
+                mCloseGuard.warnIfOpen();
                 close();
             } finally {
                 super.finalize();
@@ -22871,15 +22682,12 @@
         final int callingUid = Binder.getCallingUid();
         final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
         final int moveId = mNextMoveId.getAndIncrement();
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
-                } catch (PackageManagerException e) {
-                    Slog.w(TAG, "Failed to move " + packageName, e);
-                    mMoveCallbacks.notifyStatusChanged(moveId, e.error);
-                }
+        mHandler.post(() -> {
+            try {
+                movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
+            } catch (PackageManagerException e) {
+                Slog.w(TAG, "Failed to move " + packageName, e);
+                mMoveCallbacks.notifyStatusChanged(moveId, e.error);
             }
         });
         return moveId;
@@ -23079,24 +22887,21 @@
         final MoveInfo move;
         if (moveCompleteApp) {
             // Kick off a thread to report progress estimates
-            new Thread() {
-                @Override
-                public void run() {
-                    while (true) {
-                        try {
-                            if (installedLatch.await(1, TimeUnit.SECONDS)) {
-                                break;
-                            }
-                        } catch (InterruptedException ignored) {
+            new Thread(() -> {
+                while (true) {
+                    try {
+                        if (installedLatch.await(1, TimeUnit.SECONDS)) {
+                            break;
                         }
-
-                        final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
-                        final int progress = 10 + (int) MathUtils.constrain(
-                                ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
-                        mMoveCallbacks.notifyStatusChanged(moveId, progress);
+                    } catch (InterruptedException ignored) {
                     }
+
+                    final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
+                    final int progress = 10 + (int) MathUtils.constrain(
+                            ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
+                    mMoveCallbacks.notifyStatusChanged(moveId, progress);
                 }
-            }.start();
+            }).start();
 
             final String dataAppName = codeFile.getName();
             move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
@@ -23250,12 +23055,9 @@
                 if (DEBUG_CLEAN_APKS) {
                     Slog.i(TAG, "  Removing package " + packageName);
                 }
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
-                                userHandle, 0);
-                    } //end run
-                });
+                //end run
+                mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                        userHandle, 0));
             }
         }
     }
@@ -23502,12 +23304,8 @@
             // TODO Implement atomic delete if package is unused
             // It is currently possible that the package will be deleted even if it is installed
             // after this method returns.
-            mHandler.post(new Runnable() {
-                public void run() {
-                    deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
-                            0, PackageManager.DELETE_ALL_USERS);
-                }
-            });
+            mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                    0, PackageManager.DELETE_ALL_USERS));
         }
     }
 
@@ -24359,11 +24157,9 @@
             String[] packageNames, int userId) {
         enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledTelephonyDataServices");
         synchronized (mPackages) {
-            Binder.withCleanCallingIdentity( () -> {
-                mDefaultPermissionPolicy.
-                        grantDefaultPermissionsToEnabledTelephonyDataServices(
-                                packageNames, userId);
-            });
+            Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+                    grantDefaultPermissionsToEnabledTelephonyDataServices(
+                            packageNames, userId));
         }
     }
 
@@ -24372,11 +24168,9 @@
             String[] packageNames, int userId) {
         enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromDisabledTelephonyDataServices");
         synchronized (mPackages) {
-            Binder.withCleanCallingIdentity( () -> {
-                mDefaultPermissionPolicy.
-                        revokeDefaultPermissionsFromDisabledTelephonyDataServices(
-                                packageNames, userId);
-            });
+            Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+                    revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+                            packageNames, userId));
         }
     }
 
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index c3b9841..ea34346 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -197,6 +197,7 @@
     public SliceSpec[] getPinnedSpecs(Uri uri, String pkg) throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
+        uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
         return getPinnedSlice(uri).getSpecs();
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4413666..8a135b8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -20,6 +20,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.ITrustListener;
 import android.app.trust.ITrustManager;
 import android.content.BroadcastReceiver;
@@ -130,8 +131,8 @@
     private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray();
 
     // set to true only if user can skip bouncer
-    @GuardedBy("mUsersUnlockedByFingerprint")
-    private final SparseBooleanArray mUsersUnlockedByFingerprint = new SparseBooleanArray();
+    @GuardedBy("mUsersUnlockedByBiometric")
+    private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
 
     private final StrongAuthTracker mStrongAuthTracker;
 
@@ -440,11 +441,11 @@
             boolean secure = mLockPatternUtils.isSecure(id);
             boolean trusted = aggregateIsTrusted(id);
             boolean showingKeyguard = true;
-            boolean fingerprintAuthenticated = false;
+            boolean biometricAuthenticated = false;
 
             if (mCurrentUser == id) {
-                synchronized(mUsersUnlockedByFingerprint) {
-                    fingerprintAuthenticated = mUsersUnlockedByFingerprint.get(id, false);
+                synchronized(mUsersUnlockedByBiometric) {
+                    biometricAuthenticated = mUsersUnlockedByBiometric.get(id, false);
                 }
                 try {
                     showingKeyguard = wm.isKeyguardLocked();
@@ -452,7 +453,7 @@
                 }
             }
             boolean deviceLocked = secure && showingKeyguard && !trusted &&
-                    !fingerprintAuthenticated;
+                    !biometricAuthenticated;
             setDeviceLockedForUser(id, deviceLocked);
         }
     }
@@ -1021,20 +1022,20 @@
         }
 
         @Override
-        public void unlockedByFingerprintForUser(int userId) {
+        public void unlockedByBiometricForUser(int userId, BiometricSourceType biometricSource) {
             enforceReportPermission();
-            synchronized(mUsersUnlockedByFingerprint) {
-                mUsersUnlockedByFingerprint.put(userId, true);
+            synchronized(mUsersUnlockedByBiometric) {
+                mUsersUnlockedByBiometric.put(userId, true);
             }
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
                     0 /* arg2 */).sendToTarget();
         }
 
         @Override
-        public void clearAllFingerprints() {
+        public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
             enforceReportPermission();
-            synchronized(mUsersUnlockedByFingerprint) {
-                mUsersUnlockedByFingerprint.clear();
+            synchronized(mUsersUnlockedByBiometric) {
+                mUsersUnlockedByBiometric.clear();
             }
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
                     0 /* arg2 */).sendToTarget();
@@ -1188,8 +1189,8 @@
                     synchronized (mTrustUsuallyManagedForUser) {
                         mTrustUsuallyManagedForUser.delete(userId);
                     }
-                    synchronized (mUsersUnlockedByFingerprint) {
-                        mUsersUnlockedByFingerprint.delete(userId);
+                    synchronized (mUsersUnlockedByBiometric) {
+                        mUsersUnlockedByBiometric.delete(userId);
                     }
                     refreshAgentList(userId);
                     refreshDeviceLockedForUser(userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6686b80..0c65518 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -368,14 +368,25 @@
      *                         to be synchronized with state in WindowManagerService.
      * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
      *                                   key is expected to dismiss the software keyboard.
-     * @param targetWindowToken token to identify the target window that the IME is associated with.
-     *                          {@code null} when application, system, or the IME itself decided to
-     *                          change its window visibility before being associated with any target
-     *                          window.
      */
     public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-            boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
-            @Nullable IBinder targetWindowToken);
+            boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed);
+
+    /**
+     * Notifies WindowManagerService that the current IME window status is being changed.
+     *
+     * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
+     * caller of this method.</p>
+     *
+     * @param imeToken token to track the active input method. Corresponding IME windows can be
+     *                 identified by checking {@link android.view.WindowManager.LayoutParams#token}.
+     *                 Note that there is no guarantee that the corresponding window is already
+     *                 created
+     * @param imeTargetWindowToken token to identify the target window that the IME is associated
+     *                             with
+     */
+    public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+            @NonNull IBinder imeTargetWindowToken);
 
     /**
       * Returns true when the hardware keyboard is available.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f4aebcd..399078a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7363,16 +7363,18 @@
 
         @Override
         public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-                boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
-                @Nullable IBinder targetWindowToken) {
+                boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
+            mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
+        }
+
+        @Override
+        public void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+                @NonNull IBinder imeTargetWindowToken) {
             // TODO (b/34628091): Use this method to address the window animation issue.
             if (DEBUG_INPUT_METHOD) {
-                Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
-                        + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed
-                        + " imeWindowVisible=" + imeWindowVisible
-                        + " targetWindowToken=" + targetWindowToken);
+                Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
+                        + " imeTargetWindowToken=" + imeTargetWindowToken);
             }
-            mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
         }
 
         @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 20147d2..9ef806e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6259,7 +6259,7 @@
     }
 
     @Override
-    public void reportFailedFingerprintAttempt(int userHandle) {
+    public void reportFailedBiometricAttempt(int userHandle) {
         enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -6270,7 +6270,7 @@
     }
 
     @Override
-    public void reportSuccessfulFingerprintAttempt(int userHandle) {
+    public void reportSuccessfulBiometricAttempt(int userHandle) {
         enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 92c7abe..4456ed0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -75,6 +75,7 @@
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.emergency.EmergencyAffordanceService;
+import com.android.server.biometrics.face.FaceService;
 import com.android.server.biometrics.fingerprint.FingerprintService;
 import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
@@ -1512,6 +1513,12 @@
             }
             traceEnd();
 
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+                traceBeginAndSlog("StartFaceSensor");
+                mSystemServiceManager.startService(FaceService.class);
+                traceEnd();
+            }
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
                 traceBeginAndSlog("StartFingerprintSensor");
                 mSystemServiceManager.startService(FingerprintService.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
index 2241047..ca473c6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
@@ -19,20 +19,26 @@
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
 
 import android.app.NotificationChannel;
+import android.net.Uri;
 import android.os.Parcel;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 
 @SmallTest
@@ -68,4 +74,23 @@
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         channel.writeXml(serializer);
     }
+
+    @Test
+    public void testBackupEmptySound() throws Exception {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        channel.setSound(Uri.EMPTY, null);
+
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        channel.writeXmlForBackup(serializer, getContext());
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        restored.populateFromXmlForRestore(parser, getContext());
+
+        assertNull(restored.getSound());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 43a4e27..1db8967 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -18,6 +18,7 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -59,7 +60,7 @@
 public class SliceManagerServiceTest extends UiServiceTestCase {
 
     private static final String AUTH = "com.android.services.uitests";
-    private static final Uri TEST_URI = maybeAddUserId(Uri.parse("content://" + AUTH + "/path"), 0);
+    private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path");
 
     private static final SliceSpec[] EMPTY_SPECS = new SliceSpec[]{
     };
@@ -93,7 +94,7 @@
 
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
-        verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), anyString());
+        verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
     }
 
     @Test
@@ -126,4 +127,19 @@
         verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void testNoPinThrow() throws Exception {
+        mService.getPinnedSpecs(TEST_URI, "pkg");
+    }
+
+    @Test
+    public void testGetPinnedSpecs() throws Exception {
+        SliceSpec[] specs = new SliceSpec[] {
+            new SliceSpec("Something", 1) };
+        mService.pinSlice("pkg", TEST_URI, specs, mToken);
+
+        when(mCreatedSliceState.getSpecs()).thenReturn(specs);
+        assertEquals(specs, mService.getPinnedSpecs(TEST_URI, "pkg"));
+    }
+
 }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 2d096c0..8283e97 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3542,6 +3542,12 @@
             public static final String ICCID_PREFIX = "iccid_prefix";
 
             /**
+             * Certificate for carrier privilege access rules.
+             * <P>Type: TEXT in hex string </P>
+             */
+            public static final String PRIVILEGE_ACCESS_RULE = "privilege_access_rule";
+
+            /**
              * The {@code content://} URI for this table.
              */
             public static final Uri CONTENT_URI = Uri.parse("content://carrier_id/all");
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 66ccc6c..af44b7e 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -147,6 +147,8 @@
 
     boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
 
+    void notifyUserOfApBandConversion(String packageName);
+
     Messenger getWifiServiceMessenger(String packageName);
 
     void enableTdls(String remoteIPAddress, boolean enable);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 25f35d0..6963ed0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2170,6 +2170,21 @@
     }
 
     /**
+     * Method that triggers a notification to the user about a conversion to their saved AP config.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void notifyUserOfApBandConversion() {
+        Log.d(TAG, "apBand was converted, notify the user");
+        try {
+            mService.notifyUserOfApBandConversion(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Enable/Disable TDLS on a specific local route.
      *
      * <p>