Merge "Track Keymaster changes." into mnc-dev
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1127436..8a3c9c8 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -217,8 +217,14 @@
     public static final int OP_READ_PHONE_STATE = 51;
     /** @hide Add voicemail messages to the voicemail content provider. */
     public static final int OP_ADD_VOICEMAIL = 52;
+    /** @hide Access APIs for SIP calling over VOIP or WiFi. */
+    public static final int OP_USE_SIP = 53;
+    /** @hide Intercept outgoing calls. */
+    public static final int OP_PROCESS_OUTGOING_CALLS = 54;
+    /** @hide User the fingerprint API. */
+    public static final int OP_USE_FINGERPRINT = 55;
     /** @hide */
-    public static final int _NUM_OP = 53;
+    public static final int _NUM_OP = 56;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -351,7 +357,10 @@
             OP_ASSIST_STRUCTURE,
             OP_ASSIST_SCREENSHOT,
             OP_READ_PHONE_STATE,
-            OP_ADD_VOICEMAIL
+            OP_ADD_VOICEMAIL,
+            OP_USE_SIP,
+            OP_PROCESS_OUTGOING_CALLS,
+            OP_USE_FINGERPRINT
     };
 
     /**
@@ -411,6 +420,9 @@
             null,
             null,
             null,
+            null,
+            null,
+            null,
             null
     };
 
@@ -471,7 +483,10 @@
             "ASSIST_STRUCTURE",
             "ASSIST_SCREENSHOT",
             "OP_READ_PHONE_STATE",
-            "ADD_VOICEMAIL"
+            "ADD_VOICEMAIL",
+            "USE_SIP",
+            "PROCESS_OUTGOING_CALLS",
+            "USE_FINGERPRINT"
     };
 
     /**
@@ -531,7 +546,10 @@
             null, // no permission for receiving assist structure
             null, // no permission for receiving assist screenshot
             Manifest.permission.READ_PHONE_STATE,
-            Manifest.permission.ADD_VOICEMAIL
+            Manifest.permission.ADD_VOICEMAIL,
+            Manifest.permission.USE_SIP,
+            Manifest.permission.PROCESS_OUTGOING_CALLS,
+            Manifest.permission.USE_FINGERPRINT
     };
 
     /**
@@ -592,7 +610,10 @@
             null, // ASSIST_STRUCTURE
             null, // ASSIST_SCREENSHOT
             null, // READ_PHONE_STATE
-            null // ADD_VOICEMAIL
+            null, // ADD_VOICEMAIL
+            null, // USE_SIP
+            null, // PROCESS_OUTGOING_CALLS
+            null  // USE_FINGERPRINT
     };
 
     /**
@@ -652,7 +673,10 @@
             false, //ASSIST_STRUCTURE
             false, //ASSIST_SCREENSHOT
             false, //READ_PHONE_STATE
-            false  //ADD_VOICEMAIL
+            false, //ADD_VOICEMAIL
+            false, // USE_SIP
+            false, // PROCESS_OUTGOING_CALLS
+            false  // USE_FINGERPRINT
     };
 
     /**
@@ -711,6 +735,9 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED
     };
 
@@ -774,6 +801,9 @@
             false,
             false,
             false,
+            false,
+            false,
+            false,
             false
     };
 
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 0cf8df1..aeddf03 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -114,6 +114,11 @@
      * the Surface provided to prepare must not be used as a target of a CaptureRequest submitted
      * to this session.</p>
      *
+     * <p>{@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}
+     * devices cannot pre-allocate output buffers; for those devices,
+     * {@link StateCallback#onSurfacePrepared} will be immediately called, and no preallocation is
+     * done.</p>
+     *
      * @param surface the output Surface for which buffers should be pre-allocated. Must be one of
      * the output Surfaces used to create this session.
      *
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index abe26ea..edad00f 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -202,6 +202,7 @@
         private static final int CAMERA_IDLE = 1;
         private static final int CAPTURE_STARTED = 2;
         private static final int RESULT_RECEIVED = 3;
+        private static final int PREPARED = 4;
 
         private final HandlerThread mHandlerThread;
         private Handler mHandler;
@@ -253,7 +254,9 @@
 
         @Override
         public void onPrepared(int streamId) {
-            // TODO
+            Message msg = getHandler().obtainMessage(PREPARED,
+                    /*arg1*/ streamId, /*arg2*/ 0);
+            getHandler().sendMessage(msg);
         }
 
         @Override
@@ -301,6 +304,11 @@
                             mCallbacks.onResultReceived(result, resultExtras);
                             break;
                         }
+                        case PREPARED: {
+                            int streamId = msg.arg1;
+                            mCallbacks.onPrepared(streamId);
+                            break;
+                        }
                         default:
                             throw new IllegalArgumentException(
                                 "Unknown callback message " + msg.what);
@@ -631,7 +639,9 @@
             return CameraBinderDecorator.ENODEV;
         }
 
-        // TODO: Implement and fire callback
+        // LEGACY doesn't support actual prepare, just signal success right away
+        mCameraCallbacks.onPrepared(streamId);
+
         return CameraBinderDecorator.NO_ERROR;
     }
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 9f344ad..779448b 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -434,7 +434,8 @@
             mAuthenticationCallback = callback;
             mCryptoObject = crypto;
             long sessionId = crypto != null ? crypto.getOpId() : 0;
-            mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags);
+            mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.w(TAG, "Remote exception while authenticating: ", e);
             if (callback != null) {
@@ -555,7 +556,7 @@
      */
     public List<Fingerprint> getEnrolledFingerprints(int userId) {
         if (mService != null) try {
-            return mService.getEnrolledFingerprints(userId);
+            return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
         }
@@ -579,7 +580,8 @@
      */
     public boolean hasEnrolledFingerprints() {
         if (mService != null) try {
-            return mService.hasEnrolledFingerprints(UserHandle.myUserId());
+            return mService.hasEnrolledFingerprints(UserHandle.myUserId(),
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
         }
@@ -595,7 +597,7 @@
         if (mService != null) {
             try {
                 long deviceId = 0; /* TODO: plumb hardware id to FPMS */
-                return mService.isHardwareDetected(deviceId);
+                return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
             }
@@ -614,7 +616,7 @@
     public long getAuthenticatorId() {
         if (mService != null) {
             try {
-                return mService.getAuthenticatorId();
+                return mService.getAuthenticatorId(mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
             }
@@ -736,7 +738,7 @@
 
     private void cancelAuthentication(CryptoObject cryptoObject) {
         if (mService != null) try {
-            mService.cancelAuthentication(mToken);
+            mService.cancelAuthentication(mToken, mContext.getOpPackageName());
         } catch (RemoteException e) {
             if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment");
         }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index c5ec08c..0484806 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -27,10 +27,10 @@
 interface IFingerprintService {
     // Authenticate the given sessionId with a fingerprint
     void authenticate(IBinder token, long sessionId, int groupId,
-            IFingerprintServiceReceiver receiver, int flags);
+            IFingerprintServiceReceiver receiver, int flags, String opPackageName);
 
     // Cancel authentication for the given sessionId
-    void cancelAuthentication(IBinder token);
+    void cancelAuthentication(IBinder token, String opPackageName);
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
@@ -46,16 +46,16 @@
     void rename(int fingerId, int groupId, String name);
 
     // Get a list of enrolled fingerprints in the given group.
-    List<Fingerprint> getEnrolledFingerprints(int groupId);
+    List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName);
 
     // Determine if HAL is loaded and ready
-    boolean isHardwareDetected(long deviceId);
+    boolean isHardwareDetected(long deviceId, String opPackageName);
 
     // Get a pre-enrollment authentication token
     long preEnroll(IBinder token);
 
     // Determine if a user has at least one enrolled fingerprint
-    boolean hasEnrolledFingerprints(int groupId);
+    boolean hasEnrolledFingerprints(int groupId, String opPackageName);
 
     // Gets the number of hardware devices
     // int getHardwareDeviceCount();
@@ -64,5 +64,5 @@
     // long getHardwareDevice(int i);
 
     // Gets the authenticator ID for fingerprint
-    long getAuthenticatorId();
+    long getAuthenticatorId(String opPackageName);
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 355ec8c..009649f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -634,6 +634,9 @@
             if ((debugFlags & Zygote.DEBUG_ENABLE_JIT) != 0) {
                 argsForZygote.add("--enable-jit");
             }
+            if ((debugFlags & Zygote.DEBUG_GENERATE_CFI) != 0) {
+                argsForZygote.add("--generate-cfi");
+            }
             if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                 argsForZygote.add("--enable-assert");
             }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 75b6446..1e7ee5a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -40,6 +40,8 @@
     public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
     /** enable the JIT compiler */
     public static final int DEBUG_ENABLE_JIT         = 1 << 5;
+    /** Force generation of CFI code */
+    public static final int DEBUG_GENERATE_CFI       = 1 << 6;
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 9106ccd..969d236 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -321,7 +321,7 @@
 
         /**
          * From --enable-debugger, --enable-checkjni, --enable-assert,
-         * --enable-safemode, --enable-jit, and --enable-jni-logging.
+         * --enable-safemode, --enable-jit, --generate-cfi and --enable-jni-logging.
          */
         int debugFlags;
 
@@ -433,6 +433,8 @@
                     debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
                 } else if (arg.equals("--enable-jit")) {
                     debugFlags |= Zygote.DEBUG_ENABLE_JIT;
+                } else if (arg.equals("--generate-cfi")) {
+                    debugFlags |= Zygote.DEBUG_GENERATE_CFI;
                 } else if (arg.equals("--enable-jni-logging")) {
                     debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
                 } else if (arg.equals("--enable-assert")) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 88f0697..7c2b28d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -875,6 +875,19 @@
     parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
                        "-Xzygote-max-boot-retry=");
 
+    /*
+     * When running with debug.gencfi, add --include-cfi to the compiler options so that the boot
+     * image, if it is compiled on device, will include CFI info, as well as other compilations
+     * started by the runtime.
+     */
+    property_get("debug.gencfi", propBuf, "");
+    if (strcmp(propBuf, "true") == 0) {
+        addOption("-Xcompiler-option");
+        addOption("--include-cfi");
+        addOption("-Ximage-compiler-option");
+        addOption("--include-cfi");
+    }
+
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
     initArgs.nOptions = mOptions.size();
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index fc05a6d..3655adc 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -111,6 +111,7 @@
     jfieldID    mRouteFlags;
     jfieldID    mRegistrationId;
     jfieldID    mMixType;
+    jfieldID    mCallbackFlags;
 } gAudioMixFields;
 
 static jclass gAudioFormatClass;
@@ -149,6 +150,10 @@
     jmethodID    postEventFromNative;
 } gAudioPortEventHandlerMethods;
 
+static struct {
+    jmethodID postDynPolicyEventFromNative;
+} gDynPolicyEventHandlerMethods;
+
 static Mutex gLock;
 
 enum AudioError {
@@ -166,7 +171,7 @@
 #define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5
 
 // ----------------------------------------------------------------------------
-// ref-counted object for callbacks
+// ref-counted object for audio port callbacks
 class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
 {
 public:
@@ -361,6 +366,26 @@
     env->DeleteLocalRef(clazz);
 }
 
+static void
+android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    if (env == NULL) {
+        return;
+    }
+
+    jclass clazz = env->FindClass(kClassPathName);
+    const char* zechars = regId.string();
+    jstring zestring = env->NewStringUTF(zechars);
+
+    env->CallStaticVoidMethod(clazz, gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative,
+            event, zestring, val);
+
+    env->ReleaseStringUTFChars(zestring, zechars);
+    env->DeleteLocalRef(clazz);
+
+}
+
 static jint
 android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name)
 {
@@ -1402,7 +1427,11 @@
     return (jint)AudioSystem::getAudioHwSyncForSession((audio_session_t)sessionId);
 }
 
-
+static void
+android_media_AudioSystem_registerDynPolicyCallback(JNIEnv *env, jobject thiz)
+{
+    AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback);
+}
 
 
 static jint convertAudioMixToNative(JNIEnv *env,
@@ -1419,6 +1448,8 @@
     env->ReleaseStringUTFChars(jRegistrationId, nRegistrationId);
     env->DeleteLocalRef(jRegistrationId);
 
+    nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
+
     jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
     nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
                                                      gAudioFormatFields.mSampleRate);
@@ -1567,7 +1598,8 @@
                                     (void *)android_media_AudioSystem_getAudioHwSyncForSession},
     {"registerPolicyMixes",    "(Ljava/util/ArrayList;Z)I",
                                             (void *)android_media_AudioSystem_registerPolicyMixes},
-
+    {"native_register_dynamic_policy_callback", "()V",
+                                    (void *)android_media_AudioSystem_registerDynPolicyCallback},
 };
 
 
@@ -1670,6 +1702,10 @@
     gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
                                                     eventHandlerClass, "mJniCallback", "J");
 
+    gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative =
+            GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
+                    "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
+
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
     gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule",
@@ -1680,6 +1716,7 @@
     gAudioMixFields.mRegistrationId = GetFieldIDOrDie(env, audioMixClass, "mRegistrationId",
                                                       "Ljava/lang/String;");
     gAudioMixFields.mMixType = GetFieldIDOrDie(env, audioMixClass, "mMixType", "I");
+    gAudioMixFields.mCallbackFlags = GetFieldIDOrDie(env, audioMixClass, "mCallbackFlags", "I");
 
     jclass audioFormatClass = FindClassOrDie(env, "android/media/AudioFormat");
     gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass);
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 4863ed2..e9d6ebc 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1423,7 +1423,7 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const override {
-        OP_LOG("Draw RenderNode %p %s, flags %#x", mRenderNode, mRenderNode->getName());
+        OP_LOG("Draw RenderNode %p %s", mRenderNode, mRenderNode->getName());
         if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
             mRenderNode->output(level + 1);
         }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 25e6594..3dae543 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.audiopolicy.AudioMix;
+import android.util.Log;
 
 import java.util.ArrayList;
 
@@ -32,6 +33,7 @@
  */
 public class AudioSystem
 {
+    private static final String TAG = "AudioSystem";
     /* These values must be kept in sync with system/audio.h */
     /*
      * If these are modified, please also update Settings.System.VOLUME_SETTINGS
@@ -224,6 +226,48 @@
         }
     }
 
+    /**
+     * Handles events for the audio policy manager about dynamic audio policies
+     * @see android.media.audiopolicy.AudioPolicy
+     */
+    public interface DynamicPolicyCallback
+    {
+        void onDynamicPolicyMixStateUpdate(String regId, int state);
+    }
+
+    //keep in sync with include/media/AudioPolicy.h
+    private final static int DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE = 0;
+
+    private static DynamicPolicyCallback sDynPolicyCallback;
+
+    public static void setDynamicPolicyCallback(DynamicPolicyCallback cb)
+    {
+        synchronized (AudioSystem.class) {
+            sDynPolicyCallback = cb;
+            native_register_dynamic_policy_callback();
+        }
+    }
+
+    private static void dynamicPolicyCallbackFromNative(int event, String regId, int val)
+    {
+        DynamicPolicyCallback cb = null;
+        synchronized (AudioSystem.class) {
+            if (sDynPolicyCallback != null) {
+                cb = sDynPolicyCallback;
+            }
+        }
+        if (cb != null) {
+            switch(event) {
+                case DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE:
+                    cb.onDynamicPolicyMixStateUpdate(regId, val);
+                    break;
+                default:
+                    Log.e(TAG, "dynamicPolicyCallbackFromNative: unknown event " + event);
+            }
+        }
+    }
+
+
     /*
      * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
      * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h
@@ -580,6 +624,9 @@
     public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
     public static native int setAudioPortConfig(AudioPortConfig config);
 
+    // declare this instance as having a dynamic policy callback handler
+    private static native final void native_register_dynamic_policy_callback();
+
     // must be kept in sync with value in include/system/audio.h
     public static final int AUDIO_HW_SYNC_INVALID = 0;
 
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 6aa4d8a..4ffac6d 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -36,20 +36,30 @@
     private int mRouteFlags;
     private String mRegistrationId;
     private int mMixType = MIX_TYPE_INVALID;
-    private int mMixState = MIX_STATE_DISABLED;
+    int mMixState = MIX_STATE_DISABLED;
+    int mCallbackFlags;
 
     /**
      * All parameters are guaranteed valid through the Builder.
      */
-    private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags) {
+    private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags) {
         mRule = rule;
         mFormat = format;
         mRouteFlags = routeFlags;
         mRegistrationId = null;
         mMixType = rule.getTargetMixType();
+        mCallbackFlags = callbackFlags;
     }
 
-    // ROUTE_FLAG_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
+    // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined
+    // in frameworks/av/include/media/AudioPolicy.h
+    /** @hide */
+    public final static int CALLBACK_FLAG_NOTIFY_ACTIVITY = 0x1;
+    // when adding new MIX_FLAG_* flags, add them to this mask of authorized masks:
+    private final static int CALLBACK_FLAGS_ALL = CALLBACK_FLAG_NOTIFY_ACTIVITY;
+
+    // ROUTE_FLAG_* values: keep in sync with MIX_ROUTE_FLAG_* values defined
+    // in frameworks/av/include/media/AudioPolicy.h
     /**
      * An audio mix behavior where the output of the mix is sent to the original destination of
      * the audio signal, i.e. an output device for an output mix, or a recording for an input mix.
@@ -161,6 +171,7 @@
         private AudioMixingRule mRule = null;
         private AudioFormat mFormat = null;
         private int mRouteFlags = 0;
+        private int mCallbackFlags = 0;
 
         /**
          * @hide
@@ -199,6 +210,22 @@
         }
 
         /**
+         * @hide
+         * Only used by AudioPolicyConfig, not a public API.
+         * @param callbackFlags which callbacks are called from native
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        public Builder setCallbackFlags(int flags) throws IllegalArgumentException {
+            if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) {
+                throw new IllegalArgumentException("Illegal callback flags 0x"
+                        + Integer.toHexString(flags).toUpperCase());
+            }
+            mCallbackFlags = flags;
+            return this;
+        }
+
+        /**
          * Sets the {@link AudioFormat} for the mix.
          * @param format a non-null {@link AudioFormat} instance.
          * @return the same Builder instance.
@@ -256,7 +283,7 @@
                 }
                 mFormat = new AudioFormat.Builder().setSampleRate(rate).build();
             }
-            return new AudioMix(mRule, mFormat, mRouteFlags);
+            return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags);
         }
     }
 }
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index f128044..423b467 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -189,6 +189,12 @@
 
         @SystemApi
         public AudioPolicy build() {
+            if (mStatusListener != null) {
+                // the AudioPolicy status listener includes updates on each mix activity state
+                for (AudioMix mix : mMixes) {
+                    mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
+                }
+            }
             return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
                     mFocusListener, mStatusListener);
         }
@@ -432,6 +438,18 @@
                         + afi.getClientId() + "wasNotified=" + wasNotified);
             }
         }
+
+        public void notifyMixStateUpdate(String regId, int state) {
+            for (AudioMix mix : mConfig.getMixes()) {
+                if (mix.getRegistration().equals(regId)) {
+                    mix.mMixState = state;
+                    sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/);
+                    if (DEBUG) {
+                        Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state);
+                    }
+                }
+            }
+        }
     };
 
     //==================================================
@@ -440,6 +458,7 @@
     private final static int MSG_POLICY_STATUS_CHANGE = 0;
     private final static int MSG_FOCUS_GRANT = 1;
     private final static int MSG_FOCUS_LOSS = 2;
+    private final static int MSG_MIX_STATE_UPDATE = 3;
 
     private class EventHandler extends Handler {
         public EventHandler(AudioPolicy ap, Looper looper) {
@@ -464,6 +483,11 @@
                                 (AudioFocusInfo) msg.obj, msg.arg1 != 0);
                     }
                     break;
+                case MSG_MIX_STATE_UPDATE:
+                    if (mStatusListener != null) {
+                        mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
+                    }
+                    break;
                 default:
                     Log.e(TAG, "Unknown event " + msg.what);
             }
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 917e07b..252f5f4 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -59,6 +59,10 @@
         mMixes.add(mix);
     }
 
+    public ArrayList<AudioMix> getMixes() {
+        return mMixes;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mMixes);
@@ -75,6 +79,8 @@
         for (AudioMix mix : mMixes) {
             // write mix route flags
             dest.writeInt(mix.getRouteFlags());
+            // write callback flags
+            dest.writeInt(mix.mCallbackFlags);
             // write mix format
             dest.writeInt(mix.getFormat().getSampleRate());
             dest.writeInt(mix.getFormat().getEncoding());
@@ -96,6 +102,8 @@
             // read mix route flags
             int routeFlags = in.readInt();
             mixBuilder.setRouteFlags(routeFlags);
+            // read callback flags
+            mixBuilder.setCallbackFlags(in.readInt());
             // read mix format
             int sampleRate = in.readInt();
             int encoding = in.readInt();
diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
index c777c58..ad8af15 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -25,4 +25,7 @@
     // callbacks for audio focus
     void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult);
     void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified);
+
+    // callback for mix activity status update
+    void notifyMixStateUpdate(in String regId, int state);
 }
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8b50774..f383a52 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -890,23 +890,32 @@
     <!-- Monitoring dialog device owner body text [CHAR LIMIT=400] -->
     <string name="monitoring_description_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information. For more information, contact your administrator.</string>
 
-    <!-- Monitoring dialog profile owner body text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string>
-
-    <!-- Monitoring dialog device and profile owner body text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_device_and_profile_owned">Your device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>.\nYour work profile is managed by:\n<xliff:g id="organization">%2$s</xliff:g>.\n\nYour administrator can monitor your device and network activity, including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string>
-
     <!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and secure websites.</string>
+    <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and websites.</string>
 
     <!-- Monitoring dialog VPN with device owner text [CHAR LIMIT=400] -->
     <string name="monitoring_description_vpn_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to a VPN, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
 
     <!-- Monitoring dialog VPN with profile owner text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and secure websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
+    <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
 
-    <!-- Monitoring dialog VPN with device and profile owner text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_vpn_device_and_profile_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\nYour work profile is managed by:\n<xliff:g id="organization">%2$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and secure websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your personal network activity</string>
+    <!-- Name for a generic legacy VPN connection [CHAR LIMIT=20] -->
+    <string name="legacy_vpn_name">VPN</string>
+
+    <!-- Monitoring dialog text for single app (no profile or device owner) [CHAR LIMIT=400] -->
+    <string name="monitoring_description_app">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your network activity including emails, apps and websites.</string>
+
+    <!-- Monitoring dialog text for single app (inside personal profile) [CHAR LIMIT=400] -->
+    <string name="monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites.</string>
+
+    <!-- Monitoring dialog text for single app (inside work profile) [CHAR LIMIT=400] -->
+    <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nFor more information, contact your administrator.</string>
+
+    <!-- Monitoring dialog text for multiple apps (in personal and work profiles) [CHAR LIMIT=400] -->
+    <string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
+
+    <!-- Monitoring dialog text for single app (with device owner) [CHAR LIMIT=400] -->
+    <string name="monitoring_description_vpn_app_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
 
     <!-- Indication on the keyguard that appears when the user disables trust agents until the next time they unlock manually. [CHAR LIMIT=NONE] -->
     <string name="keyguard_indication_trust_disabled">Device will stay locked until you manually unlock</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index d8e3984..f59e864 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -110,21 +110,15 @@
     }
 
     private void handleRefreshState() {
-        if (mSecurityController.hasDeviceOwner()) {
+        boolean hasDeviceOwner = mSecurityController.hasDeviceOwner();
+        boolean hasVpn = mSecurityController.isVpnEnabled();
+
+        mIsVisible = (hasVpn || hasDeviceOwner);
+        mIsIconVisible = hasVpn;
+        if (hasDeviceOwner) {
             mFooterTextId = R.string.device_owned_footer;
-            mIsVisible = true;
-            mIsIconVisible = false;
-        } else if (mSecurityController.hasProfileOwner()) {
-            mFooterTextId = R.string.profile_owned_footer;
-            mIsVisible = true;
-            mIsIconVisible = false;
-        } else if (mSecurityController.isVpnEnabled()) {
-            mFooterTextId = R.string.vpn_footer;
-            mIsVisible = true;
-            mIsIconVisible = true;
         } else {
-            mIsVisible = false;
-            mIsIconVisible = false;
+            mFooterTextId = R.string.vpn_footer;
         }
         mMainHandler.post(mUpdateDisplayState);
     }
@@ -162,37 +156,17 @@
 
     private String getMessage(boolean hasDeviceOwner, boolean hasProfile, boolean hasVpn) {
         if (hasDeviceOwner) {
-            if (hasProfile) {
-                if (hasVpn) {
-                    return mContext.getString(
-                            R.string.monitoring_description_vpn_device_and_profile_owned,
-                            mSecurityController.getDeviceOwnerName(),
-                            mSecurityController.getProfileOwnerName());
-                } else {
-                    return mContext.getString(
-                            R.string.monitoring_description_device_and_profile_owned,
-                            mSecurityController.getDeviceOwnerName(),
-                            mSecurityController.getProfileOwnerName());
-                }
+            if (hasVpn) {
+                return mContext.getString(R.string.monitoring_description_vpn_device_owned,
+                        mSecurityController.getDeviceOwnerName());
             } else {
-                if (hasVpn) {
-                    return mContext.getString(R.string.monitoring_description_vpn_device_owned,
-                            mSecurityController.getDeviceOwnerName());
-                } else {
-                    return mContext.getString(R.string.monitoring_description_device_owned,
-                            mSecurityController.getDeviceOwnerName());
-                }
+                return mContext.getString(R.string.monitoring_description_device_owned,
+                        mSecurityController.getDeviceOwnerName());
             }
         } else if (hasProfile) {
-            if (hasVpn) {
-                return mContext.getString(
-                        R.string.monitoring_description_vpn_profile_owned,
-                        mSecurityController.getProfileOwnerName());
-            } else {
-                return mContext.getString(
-                        R.string.monitoring_description_profile_owned,
-                        mSecurityController.getProfileOwnerName());
-            }
+            return mContext.getString(
+                    R.string.monitoring_description_vpn_profile_owned,
+                    mSecurityController.getProfileOwnerName());
         } else {
             return mContext.getString(R.string.monitoring_description_vpn);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ddcfb15..069878e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3185,6 +3185,10 @@
                     debugFlags |= Zygote.DEBUG_ENABLE_JIT;
                 }
             }
+            String genCFIDebugProperty = SystemProperties.get("debug.gencfi");
+            if ("true".equals(genCFIDebugProperty)) {
+                debugFlags |= Zygote.DEBUG_GENERATE_CFI;
+            }
             if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
                 debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
             }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index eb28ed0..06fba34 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -65,6 +65,7 @@
 import android.media.VolumePolicy;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
+import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
 import android.media.audiopolicy.IAudioPolicyCallback;
@@ -206,6 +207,7 @@
     private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = 22;
     private static final int MSG_PERSIST_MICROPHONE_MUTE = 23;
     private static final int MSG_UNMUTE_STREAM = 24;
+    private static final int MSG_DYN_POLICY_MIX_STATE_UPDATE = 25;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -4337,6 +4339,9 @@
                 case MSG_UNMUTE_STREAM:
                     onUnmuteStream(msg.arg1, msg.arg2);
                     break;
+                case MSG_DYN_POLICY_MIX_STATE_UPDATE:
+                    onDynPolicyMixStateUpdate((String) msg.obj, msg.arg1);
+                    break;
             }
         }
     }
@@ -5758,6 +5763,8 @@
     //==========================================================================================
     public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
             boolean hasFocusListener) {
+        AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
+
         if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
                 + " with config:" + policyConfig);
         String regId = null;
@@ -5853,6 +5860,39 @@
     }
 
     //======================
+    // Audio policy callback from AudioSystem
+    //======================
+    private final AudioSystem.DynamicPolicyCallback mDynPolicyCallback =
+            new AudioSystem.DynamicPolicyCallback() {
+        public void onDynamicPolicyMixStateUpdate(String regId, int state) {
+            if (!TextUtils.isEmpty(regId)) {
+                sendMsg(mAudioHandler, MSG_DYN_POLICY_MIX_STATE_UPDATE, SENDMSG_QUEUE,
+                        state /*arg1*/, 0 /*arg2 ignored*/, regId /*obj*/, 0 /*delay*/);
+            }
+        }
+    };
+
+    private void onDynPolicyMixStateUpdate(String regId, int state) {
+        if (DEBUG_AP) Log.d(TAG, "onDynamicPolicyMixStateUpdate("+ regId + ", " + state +")");
+        synchronized (mAudioPolicies) {
+            for (AudioPolicyProxy policy : mAudioPolicies.values()) {
+                for (AudioMix mix : policy.getMixes()) {
+                    if (mix.getRegistration().equals(regId)) {
+                        try {
+                            policy.mPolicyCallback.notifyMixStateUpdate(regId, state);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Can't call notifyMixStateUpdate() on IAudioPolicyCallback "
+                                    + policy.mPolicyCallback.asBinder(), e);
+                        }
+                        return;
+                    }
+                }
+            }
+        }
+
+    }
+
+    //======================
     // Audio policy proxy
     //======================
     /**
@@ -5861,8 +5901,7 @@
      */
     public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient {
         private static final String TAG = "AudioPolicyProxy";
-        AudioPolicyConfig mConfig;
-        IAudioPolicyCallback mPolicyToken;
+        IAudioPolicyCallback mPolicyCallback;
         boolean mHasFocusListener;
         /**
          * Audio focus ducking behavior for an audio policy.
@@ -5877,19 +5916,19 @@
                 boolean hasFocusListener) {
             super(config);
             setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++));
-            mPolicyToken = token;
+            mPolicyCallback = token;
             mHasFocusListener = hasFocusListener;
             if (mHasFocusListener) {
-                mMediaFocusControl.addFocusFollower(mPolicyToken);
+                mMediaFocusControl.addFocusFollower(mPolicyCallback);
             }
             connectMixes();
         }
 
         public void binderDied() {
             synchronized (mAudioPolicies) {
-                Log.i(TAG, "audio policy " + mPolicyToken + " died");
+                Log.i(TAG, "audio policy " + mPolicyCallback + " died");
                 release();
-                mAudioPolicies.remove(mPolicyToken.asBinder());
+                mAudioPolicies.remove(mPolicyCallback.asBinder());
             }
         }
 
@@ -5902,7 +5941,7 @@
                 mMediaFocusControl.setDuckingInExtPolicyAvailable(false);
             }
             if (mHasFocusListener) {
-                mMediaFocusControl.removeFocusFollower(mPolicyToken);
+                mMediaFocusControl.removeFocusFollower(mPolicyCallback);
             }
             AudioSystem.registerPolicyMixes(mMixes, false);
         }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 6b5908d..ed8519a 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -16,14 +16,15 @@
 
 package com.android.server.fingerprint;
 
+import android.app.AppOpsManager;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.os.RemoteException;
-import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.server.SystemService;
@@ -39,6 +40,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -55,6 +57,8 @@
     private ClientMonitor mEnrollClient = null;
     private ClientMonitor mRemoveClient = null;
 
+    private final AppOpsManager mAppOps;
+
     private static final int MSG_NOTIFY = 10;
 
     private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
@@ -96,6 +100,7 @@
     public FingerprintService(Context context) {
         super(context);
         mContext = context;
+        mAppOps = context.getSystemService(AppOpsManager.class);
         nativeInit(Looper.getMainLooper().getQueue(), this);
     }
 
@@ -361,6 +366,13 @@
                 "Must have " + permission + " permission.");
     }
 
+    private boolean canUserFingerPrint(String opPackageName) {
+        checkPermission(USE_FINGERPRINT);
+
+        return mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, Binder.getCallingUid(),
+                opPackageName) == AppOpsManager.MODE_ALLOWED;
+    }
+
     private class ClientMonitor implements IBinder.DeathRecipient {
         IBinder token;
         IFingerprintServiceReceiver receiver;
@@ -522,8 +534,11 @@
         @Override
         // Binder call
         public void authenticate(final IBinder token, final long opId, final int groupId,
-                final IFingerprintServiceReceiver receiver, final int flags) {
+                final IFingerprintServiceReceiver receiver, final int flags, String opPackageName) {
             checkPermission(USE_FINGERPRINT);
+            if (!canUserFingerPrint(opPackageName)) {
+                return;
+            }
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -535,8 +550,10 @@
         @Override
 
         // Binder call
-        public void cancelAuthentication(final IBinder token) {
-            checkPermission(USE_FINGERPRINT);
+        public void cancelAuthentication(final IBinder token, String opPackageName) {
+            if (!canUserFingerPrint(opPackageName)) {
+                return;
+            }
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -561,8 +578,10 @@
 
         @Override
         // Binder call
-        public boolean isHardwareDetected(long deviceId) {
-            checkPermission(USE_FINGERPRINT);
+        public boolean isHardwareDetected(long deviceId, String opPackageName) {
+            if (!canUserFingerPrint(opPackageName)) {
+                return false;
+            }
             return mHalDeviceId != 0; // TODO
         }
 
@@ -580,21 +599,27 @@
 
         @Override
         // Binder call
-        public List<Fingerprint> getEnrolledFingerprints(int groupId) {
-            checkPermission(USE_FINGERPRINT);
+        public List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName) {
+            if (!canUserFingerPrint(opPackageName)) {
+                return Collections.emptyList();
+            }
             return FingerprintService.this.getEnrolledFingerprints(groupId);
         }
 
         @Override
         // Binder call
-        public boolean hasEnrolledFingerprints(int groupId) {
-            checkPermission(USE_FINGERPRINT);
+        public boolean hasEnrolledFingerprints(int groupId, String opPackageName) {
+            if (!canUserFingerPrint(opPackageName)) {
+                return false;
+            }
             return FingerprintService.this.hasEnrolledFingerprints(groupId);
         }
 
         @Override
-        public long getAuthenticatorId() {
-            checkPermission(USE_FINGERPRINT);
+        public long getAuthenticatorId(String opPackageName) {
+            if (!canUserFingerPrint(opPackageName)) {
+                return 0;
+            }
             return nativeGetAuthenticatorId();
         }
     }