Merge "Adding API for displaying a DO/PO controlled secondary lock screen post initial system lock screen."
diff --git a/api/current.txt b/api/current.txt
index b6e9a11..1be9926 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6767,6 +6767,13 @@
     method public final android.os.IBinder onBind(android.content.Intent);
   }
 
+  public class DevicePolicyKeyguardService extends android.app.Service {
+    ctor public DevicePolicyKeyguardService();
+    method @Nullable public void dismiss();
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public android.view.SurfaceControl onSurfaceReady(@Nullable android.os.IBinder);
+  }
+
   public class DevicePolicyManager {
     method public void addCrossProfileIntentFilter(@NonNull android.content.ComponentName, android.content.IntentFilter, int);
     method public boolean addCrossProfileWidgetProvider(@NonNull android.content.ComponentName, String);
@@ -6979,6 +6986,7 @@
     method public boolean setResetPasswordToken(android.content.ComponentName, byte[]);
     method public void setRestrictionsProvider(@NonNull android.content.ComponentName, @Nullable android.content.ComponentName);
     method public void setScreenCaptureDisabled(@NonNull android.content.ComponentName, boolean);
+    method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setSecureSetting(@NonNull android.content.ComponentName, String, String);
     method public void setSecurityLoggingEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setShortSupportMessage(@NonNull android.content.ComponentName, @Nullable CharSequence);
@@ -7004,6 +7012,7 @@
     field public static final String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
     field public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE";
     field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
+    field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
     field public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
     field public static final String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
     field public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE";
diff --git a/api/system-current.txt b/api/system-current.txt
index d9ef009..52a26fd 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -845,6 +845,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
+    method public boolean isSecondaryLockscreenEnabled(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java
new file mode 100644
index 0000000..c2a76c5
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.SurfaceControl;
+
+/**
+ * Client interface for providing the SystemUI with secondary lockscreen information.
+ *
+ * <p>An implementation must be provided by the device admin app when
+ * {@link DevicePolicyManager#setSecondaryLockscreenEnabled} is set to true and the service must be
+ * declared in the manifest as handling the action
+ * {@link DevicePolicyManager#ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE}, otherwise the keyguard
+ * will fail to bind to the service and continue to unlock.
+ *
+ * @see DevicePolicyManager#setSecondaryLockscreenEnabled
+ */
+public class DevicePolicyKeyguardService extends Service {
+    private static final String TAG = "DevicePolicyKeyguardService";
+    private IKeyguardCallback mCallback;
+
+    private final IKeyguardClient mClient = new IKeyguardClient.Stub() {
+        @Override
+        public void onSurfaceReady(@Nullable IBinder hostInputToken, IKeyguardCallback callback) {
+            mCallback = callback;
+            SurfaceControl surfaceControl =
+                    DevicePolicyKeyguardService.this.onSurfaceReady(hostInputToken);
+
+            if (mCallback != null) {
+                try {
+                    mCallback.onSurfaceControlCreated(surfaceControl);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to return created SurfaceControl", e);
+                }
+            }
+        }
+    };
+
+    @Override
+    @Nullable
+    public final IBinder onBind(@Nullable Intent intent) {
+        return mClient.asBinder();
+    }
+
+    /**
+     * Called by keyguard once the host surface for the secondary lockscreen is ready to display
+     * remote content.
+     * @return the {@link SurfaceControl} for the Surface the secondary lockscreen content is
+     *      attached to.
+     */
+    @Nullable
+    public SurfaceControl onSurfaceReady(@Nullable IBinder hostInputToken) {
+        return null;
+    }
+
+    /**
+     * Signals to keyguard that the secondary lock screen is ready to be dismissed.
+     */
+    @Nullable
+    public void dismiss() {
+        try {
+            mCallback.onDismiss();
+        } catch (RemoteException e) {
+            Log.e(TAG, "onDismiss failed", e);
+        }
+    }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a35a899..d58c4eb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2384,6 +2384,13 @@
     public static final int MAX_PASSWORD_LENGTH = 16;
 
     /**
+     * Service Action: Service implemented by a device owner or profile owner to provide a
+     * secondary lockscreen.
+     */
+    public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE =
+            "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -8393,6 +8400,52 @@
     }
 
     /**
+     * Called by device owner or profile owner to set whether a secondary lockscreen needs to be
+     * shown.
+     *
+     * <p>The secondary lockscreen will by displayed after the primary keyguard security screen
+     * requirements are met. To provide the lockscreen content the DO/PO will need to provide a
+     * service handling the {@link #ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE} intent action,
+     * extending the {@link DevicePolicyKeyguardService} class.
+     *
+     * <p>Relevant interactions on the secondary lockscreen should be communicated back to the
+     * keyguard via {@link IKeyguardCallback}, such as when the screen is ready to be dismissed.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param enabled Whether or not the lockscreen needs to be shown.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #isSecondaryLockscreenEnabled
+     **/
+    public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setSecondaryLockscreenEnabled");
+        if (mService != null) {
+            try {
+                mService.setSecondaryLockscreenEnabled(admin, enabled);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns whether the secondary lock screen needs to be shown.
+     * @see #setSecondaryLockscreenEnabled
+     * @hide
+     */
+    @SystemApi
+    public boolean isSecondaryLockscreenEnabled(int userId) {
+        throwIfParentInstance("isSecondaryLockscreenEnabled");
+        if (mService != null) {
+            try {
+                return mService.isSecondaryLockscreenEnabled(userId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Sets which packages may enter lock task mode.
      * <p>
      * Any packages that share uid with an allowed package will also be allowed to activate lock
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a2c0856..e7667c0 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -252,6 +252,9 @@
     String[] getAccountTypesWithManagementDisabled();
     String[] getAccountTypesWithManagementDisabledAsUser(int userId);
 
+    void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
+    boolean isSecondaryLockscreenEnabled(int userId);
+
     void setLockTaskPackages(in ComponentName who, in String[] packages);
     String[] getLockTaskPackages(in ComponentName who);
     boolean isLockTaskPermitted(in String pkg);
diff --git a/core/java/android/app/admin/IKeyguardCallback.aidl b/core/java/android/app/admin/IKeyguardCallback.aidl
new file mode 100644
index 0000000..81e7d4d
--- /dev/null
+++ b/core/java/android/app/admin/IKeyguardCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import android.view.SurfaceControl;
+
+/**
+ * Internal IPC interface for informing the keyguard of events on the secondary lockscreen.
+ * @hide
+ */
+interface IKeyguardCallback {
+    oneway void onSurfaceControlCreated(in SurfaceControl remoteSurfaceControl);
+    oneway void onDismiss();
+}
diff --git a/core/java/android/app/admin/IKeyguardClient.aidl b/core/java/android/app/admin/IKeyguardClient.aidl
new file mode 100644
index 0000000..4bfd990
--- /dev/null
+++ b/core/java/android/app/admin/IKeyguardClient.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import android.app.admin.IKeyguardCallback;
+
+/**
+ * Internal IPC interface for a service to provide the SystemUI with secondary lockscreen
+ * information.
+ * @hide
+ */
+interface IKeyguardClient {
+    oneway void onSurfaceReady(in IBinder hostInputToken, in IKeyguardCallback keyguardCallback);
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
new file mode 100644
index 0000000..2f8ef2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import android.annotation.Nullable;
+import android.app.admin.IKeyguardCallback;
+import android.app.admin.IKeyguardClient;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Encapsulates all logic for secondary lockscreen state management.
+ */
+public class AdminSecondaryLockScreenController {
+    private static final String TAG = "AdminSecondaryLockScreenController";
+    private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500;
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final Context mContext;
+    private final ViewGroup mParent;
+    private AdminSecurityView mView;
+    private Handler mHandler;
+    private IKeyguardClient mClient;
+    private KeyguardSecurityCallback mKeyguardCallback;
+    private SurfaceControl.Transaction mTransaction;
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mClient = IKeyguardClient.Stub.asInterface(service);
+            if (mView.isAttachedToWindow() && mClient != null) {
+                onSurfaceReady();
+
+                try {
+                    service.linkToDeath(mKeyguardClientDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    // Failed to link to death, just dismiss and unbind the service for now.
+                    Log.e(TAG, "Lost connection to secondary lockscreen service", e);
+                    dismiss(KeyguardUpdateMonitor.getCurrentUser());
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mClient = null;
+        }
+    };
+
+    private final IBinder.DeathRecipient mKeyguardClientDeathRecipient = () -> {
+        hide(); // hide also takes care of unlinking to death.
+        Log.d(TAG, "KeyguardClient service died");
+    };
+
+    private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() {
+        @Override
+        public void onDismiss() {
+            dismiss(UserHandle.getCallingUserId());
+        }
+
+        @Override
+        public void onSurfaceControlCreated(@Nullable SurfaceControl remoteSurfaceControl) {
+            if (mHandler != null) {
+                mHandler.removeCallbacksAndMessages(null);
+            }
+            if (remoteSurfaceControl != null) {
+                mTransaction.reparent(remoteSurfaceControl, mView.getSurfaceControl())
+                    .apply();
+            } else {
+                dismiss(KeyguardUpdateMonitor.getCurrentUser());
+            }
+        }
+    };
+
+    private final KeyguardUpdateMonitorCallback mUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onSecondaryLockscreenRequirementChanged(int userId) {
+                    Intent newIntent = mUpdateMonitor.getSecondaryLockscreenRequirement(userId);
+                    if (newIntent == null) {
+                        dismiss(userId);
+                    }
+                }
+            };
+
+    @VisibleForTesting
+    protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            final int userId = KeyguardUpdateMonitor.getCurrentUser();
+            mUpdateMonitor.registerCallback(mUpdateCallback);
+
+            if (mClient != null) {
+                onSurfaceReady();
+            }
+            mHandler.postDelayed(
+                    () -> {
+                        // If the remote content is not readied within the timeout period,
+                        // move on without the secondary lockscreen.
+                        dismiss(userId);
+                    },
+                    REMOTE_CONTENT_READY_TIMEOUT_MILLIS);
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mUpdateMonitor.removeCallback(mUpdateCallback);
+        }
+    };
+
+    public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
+            KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
+            Handler handler, SurfaceControl.Transaction transaction) {
+        mContext = context;
+        mHandler = handler;
+        mParent = parent;
+        mTransaction = transaction;
+        mUpdateMonitor = updateMonitor;
+        mKeyguardCallback = callback;
+        mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
+    }
+
+    /**
+     * Displays the Admin security Surface view.
+     */
+    public void show(Intent serviceIntent) {
+        mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
+        mParent.addView(mView);
+    }
+
+    /**
+     * Hides the Admin security Surface view.
+     */
+    public void hide() {
+        if (mView.isAttachedToWindow()) {
+            mParent.removeView(mView);
+        }
+        if (mClient != null) {
+            mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0);
+            mContext.unbindService(mConnection);
+            mClient = null;
+        }
+    }
+
+    private void onSurfaceReady() {
+        try {
+            mClient.onSurfaceReady(mView.getInputToken(), mCallback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in onSurfaceReady", e);
+            dismiss(KeyguardUpdateMonitor.getCurrentUser());
+        }
+    }
+
+    private void dismiss(int userId) {
+        mHandler.removeCallbacksAndMessages(null);
+        if (mView != null && mView.isAttachedToWindow()
+                && userId == KeyguardUpdateMonitor.getCurrentUser()) {
+            hide();
+            mKeyguardCallback.dismiss(true, userId);
+        }
+    }
+
+    /**
+     * Custom {@link SurfaceView} used to allow a device admin to present an additional security
+     * screen.
+     */
+    private class AdminSecurityView extends SurfaceView {
+        private SurfaceHolder.Callback mSurfaceHolderCallback;
+
+        AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback) {
+            super(context);
+            mSurfaceHolderCallback = surfaceHolderCallback;
+            setZOrderOnTop(true);
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            getHolder().addCallback(mSurfaceHolderCallback);
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            getHolder().removeCallback(mSurfaceHolderCallback);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 9ae446e..ae78726 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,9 +21,12 @@
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -31,6 +34,7 @@
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -90,6 +94,7 @@
     private AlertDialog mAlertDialog;
     private InjectionInflationController mInjectionInflationController;
     private boolean mSwipeUpToRetry;
+    private AdminSecondaryLockScreenController mSecondaryLockScreenController;
 
     private final ViewConfiguration mViewConfiguration;
     private final SpringAnimation mSpringAnimation;
@@ -137,6 +142,9 @@
             SystemUIFactory.getInstance().getRootComponent());
         mViewConfiguration = ViewConfiguration.get(context);
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
+        mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
+                mUpdateMonitor, mCallback, new Handler(Looper.myLooper()),
+                new SurfaceControl.Transaction());
     }
 
     public void setSecurityCallback(SecurityCallback callback) {
@@ -157,6 +165,7 @@
             mAlertDialog.dismiss();
             mAlertDialog = null;
         }
+        mSecondaryLockScreenController.hide();
         if (mCurrentSecuritySelection != SecurityMode.None) {
             getSecurityView(mCurrentSecuritySelection).onPause();
         }
@@ -532,6 +541,15 @@
                     break;
             }
         }
+        // Check for device admin specified additional security measures.
+        if (finish) {
+            Intent secondaryLockscreenIntent =
+                    mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
+            if (secondaryLockscreenIntent != null) {
+                mSecondaryLockScreenController.show(secondaryLockscreenIntent);
+                return false;
+            }
+        }
         if (eventSubtype != -1) {
             mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
                     .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
@@ -751,6 +769,5 @@
     public void showUsabilityHint() {
         mSecurityViewFlipper.showUsabilityHint();
     }
-
 }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 65fc215..f03648a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -113,6 +113,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TimeZone;
 import java.util.function.Consumer;
@@ -334,6 +335,7 @@
     private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
     private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
     private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
+    private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>();
 
     private static int sCurrentUser;
     private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
@@ -928,6 +930,45 @@
         return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
     }
 
+    private void updateSecondaryLockscreenRequirement(int userId) {
+        Intent oldIntent = mSecondaryLockscreenRequirement.get(userId);
+        boolean enabled = mDevicePolicyManager.isSecondaryLockscreenEnabled(userId);
+        boolean changed = false;
+
+        if (enabled && (oldIntent == null)) {
+            ResolveInfo resolveInfo =
+                    mContext.getPackageManager().resolveService(
+                            new Intent(
+                                    DevicePolicyManager.ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE),
+                            0);
+            if (resolveInfo != null) {
+                Intent newIntent = new Intent();
+                newIntent.setComponent(resolveInfo.serviceInfo.getComponentName());
+                mSecondaryLockscreenRequirement.put(userId, newIntent);
+                changed = true;
+            }
+        } else if (!enabled && (oldIntent != null)) {
+            mSecondaryLockscreenRequirement.put(userId, null);
+            changed = true;
+        }
+        if (changed) {
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+                if (cb != null) {
+                    cb.onSecondaryLockscreenRequirementChanged(userId);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns an Intent by which to bind to a service that will provide additional security screen
+     * content that must be shown prior to dismissing the keyguard for this user.
+     */
+    public Intent getSecondaryLockscreenRequirement(int userId) {
+        return mSecondaryLockscreenRequirement.get(userId);
+    }
+
     /**
      * Cached version of {@link TrustManager#isTrustUsuallyManaged(int)}.
      */
@@ -1113,7 +1154,8 @@
                         getSendingUserId()));
             } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
                     .equals(action)) {
-                mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED);
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED,
+                        getSendingUserId()));
             } else if (ACTION_USER_UNLOCKED.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_UNLOCKED,
                         getSendingUserId(), 0));
@@ -1530,7 +1572,7 @@
                         handleDeviceProvisioned();
                         break;
                     case MSG_DPM_STATE_CHANGED:
-                        handleDevicePolicyManagerStateChanged();
+                        handleDevicePolicyManagerStateChanged(msg.arg1);
                         break;
                     case MSG_USER_SWITCHING:
                         handleUserSwitching(msg.arg1, (IRemoteCallback) msg.obj);
@@ -1706,6 +1748,7 @@
         mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
         mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
         mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
+        updateSecondaryLockscreenRequirement(user);
         List<UserInfo> allUsers = mUserManager.getUsers();
         for (UserInfo userInfo : allUsers) {
             mUserTrustIsUsuallyManaged.put(userInfo.id,
@@ -2046,9 +2089,10 @@
     /**
      * Handle {@link #MSG_DPM_STATE_CHANGED}
      */
-    private void handleDevicePolicyManagerStateChanged() {
+    private void handleDevicePolicyManagerStateChanged(int userId) {
         checkIsHandlerThread();
         updateFingerprintListeningState();
+        updateSecondaryLockscreenRequirement(userId);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 04502f0..8e87b7a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -310,4 +310,9 @@
      */
     public void onBiometricsCleared() { }
 
+    /**
+     * Called when the secondary lock screen requirement changes.
+     */
+    public void onSecondaryLockscreenRequirementChanged(int userId) { }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
new file mode 100644
index 0000000..1954b39
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.AdditionalAnswers.answerVoid;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.IKeyguardCallback;
+import android.app.admin.IKeyguardClient;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+@RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
+
+    private static final int TARGET_USER_ID = KeyguardUpdateMonitor.getCurrentUser();
+
+    private AdminSecondaryLockScreenController mTestController;
+    private ComponentName mComponentName;
+    private Intent mServiceIntent;
+    private TestableLooper mTestableLooper;
+    private ViewGroup mParent;
+
+    @Mock
+    private Handler mHandler;
+    @Mock
+    private IKeyguardClient.Stub mKeyguardClient;
+    @Mock
+    private KeyguardSecurityCallback mKeyguardCallback;
+    @Mock
+    private KeyguardUpdateMonitor mUpdateMonitor;
+    @Spy
+    private StubTransaction mTransaction;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mParent = spy(new FrameLayout(mContext));
+        ViewUtils.attachView(mParent);
+
+        mTestableLooper = TestableLooper.get(this);
+        mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class");
+        mServiceIntent = new Intent().setComponent(mComponentName);
+
+        mContext.addMockService(mComponentName, mKeyguardClient);
+        // Have Stub.asInterface return the mocked interface.
+        when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient);
+        when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
+
+        mTestController = new AdminSecondaryLockScreenController(
+                mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler, mTransaction);
+    }
+
+    @Test
+    public void testShow() throws Exception {
+        doAnswer(invocation -> {
+            IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
+            callback.onSurfaceControlCreated(new SurfaceControl());
+            return null;
+        }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
+
+        mTestController.show(mServiceIntent);
+
+        verifySurfaceReady();
+        verify(mTransaction).reparent(any(), any());
+        assertThat(mContext.isBound(mComponentName)).isTrue();
+    }
+
+    @Test
+    public void testShow_dismissedByCallback() throws Exception {
+        doAnswer(invocation -> {
+            IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
+            callback.onDismiss();
+            return null;
+        }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
+
+        mTestController.show(mServiceIntent);
+
+        verifyViewDismissed(verifySurfaceReady());
+    }
+
+    @Test
+    public void testHide() throws Exception {
+        // Show the view first, then hide.
+        doAnswer(invocation -> {
+            IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
+            callback.onSurfaceControlCreated(new SurfaceControl());
+            return null;
+        }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
+
+        mTestController.show(mServiceIntent);
+        SurfaceView v = verifySurfaceReady();
+
+        mTestController.hide();
+        verify(mParent).removeView(v);
+        assertThat(mContext.isBound(mComponentName)).isFalse();
+    }
+
+    @Test
+    public void testHide_notShown() throws Exception {
+        mTestController.hide();
+        // Nothing should happen if trying to hide when the view isn't attached yet.
+        verify(mParent, never()).removeView(any(SurfaceView.class));
+    }
+
+    @Test
+    public void testDismissed_onSurfaceReady_RemoteException() throws Exception {
+        doThrow(new RemoteException()).when(mKeyguardClient)
+                .onSurfaceReady(any(), any(IKeyguardCallback.class));
+
+        mTestController.show(mServiceIntent);
+
+        verifyViewDismissed(verifySurfaceReady());
+    }
+
+    @Test
+    public void testDismissed_onSurfaceReady_timeout() throws Exception {
+        // Mocked KeyguardClient never handles the onSurfaceReady, so the operation times out,
+        // resulting in the view being dismissed.
+        doAnswer(answerVoid(Runnable::run)).when(mHandler)
+                .postDelayed(any(Runnable.class), anyLong());
+
+        mTestController.show(mServiceIntent);
+
+        verifyViewDismissed(verifySurfaceReady());
+    }
+
+    private SurfaceView verifySurfaceReady() throws Exception {
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class);
+        verify(mParent).addView(captor.capture());
+
+        mTestableLooper.processAllMessages();
+        verify(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
+        return captor.getValue();
+    }
+
+    private void verifyViewDismissed(SurfaceView v) throws Exception {
+        verify(mParent).removeView(v);
+        verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID);
+        assertThat(mContext.isBound(mComponentName)).isFalse();
+    }
+
+    /**
+     * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit testing to
+     * avoid calls to native code.
+     */
+    private class StubTransaction extends SurfaceControl.Transaction {
+        @Override
+        public void apply() {
+        }
+
+        @Override
+        public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
+            return this;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 12da006..b3c2ba3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -34,12 +34,16 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -519,6 +523,52 @@
         assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isFalse();
     }
 
+    @Test
+    public void testSecondaryLockscreenRequirement() {
+        int user = KeyguardUpdateMonitor.getCurrentUser();
+        String packageName = "fake.test.package";
+        String cls = "FakeService";
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = packageName;
+        serviceInfo.name = cls;
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.serviceInfo = serviceInfo;
+        when(mPackageManager.resolveService(any(Intent.class), eq(0))).thenReturn(resolveInfo);
+        when(mDevicePolicyManager.isSecondaryLockscreenEnabled(eq(user))).thenReturn(true, false);
+
+        // Initially null.
+        assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull();
+
+        // Set non-null after DPM change.
+        setBroadcastReceiverPendingResult(mKeyguardUpdateMonitor.mBroadcastAllReceiver);
+        Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+        mKeyguardUpdateMonitor.mBroadcastAllReceiver.onReceive(getContext(), intent);
+        mTestableLooper.processAllMessages();
+
+        Intent storedIntent = mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user);
+        assertThat(storedIntent.getComponent().getClassName()).isEqualTo(cls);
+        assertThat(storedIntent.getComponent().getPackageName()).isEqualTo(packageName);
+
+        // Back to null after another DPM change.
+        mKeyguardUpdateMonitor.mBroadcastAllReceiver.onReceive(getContext(), intent);
+        mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull();
+    }
+
+    private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) {
+        BroadcastReceiver.PendingResult pendingResult =
+                new BroadcastReceiver.PendingResult(Activity.RESULT_OK,
+                        "resultData",
+                        /* resultExtras= */ null,
+                        BroadcastReceiver.PendingResult.TYPE_UNREGISTERED,
+                        /* ordered= */ true,
+                        /* sticky= */ false,
+                        /* token= */ null,
+                        UserHandle.myUserId(),
+                        /* flags= */ 0);
+        receiver.setPendingResult(pendingResult);
+    }
+
     private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
         int subscription = simInited
                 ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e939d84..0c79a6f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -370,6 +370,8 @@
 
     private static final String TAG_PROTECTED_PACKAGES = "protected-packages";
 
+    private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary-lock-screen";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
@@ -771,6 +773,8 @@
 
         boolean mCurrentInputMethodSet = false;
 
+        boolean mSecondaryLockscreenEnabled = false;
+
         // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
         Set<String> mOwnerInstalledCaCerts = new ArraySet<>();
 
@@ -3322,6 +3326,12 @@
                 out.endTag(null, TAG_LOCK_TASK_FEATURES);
             }
 
+            if (policy.mSecondaryLockscreenEnabled) {
+                out.startTag(null, TAG_SECONDARY_LOCK_SCREEN);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(true));
+                out.endTag(null, TAG_SECONDARY_LOCK_SCREEN);
+            }
+
             if (policy.mStatusBarDisabled) {
                 out.startTag(null, TAG_STATUS_BAR);
                 out.attribute(null, ATTR_DISABLED, Boolean.toString(policy.mStatusBarDisabled));
@@ -3571,6 +3581,9 @@
                 } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) {
                     policy.mLockTaskFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) {
+                    policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_STATUS_BAR.equals(tag)) {
                     policy.mStatusBarDisabled = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_DISABLED));
@@ -8601,6 +8614,7 @@
         // Clear delegations.
         policy.mDelegationMap.clear();
         policy.mStatusBarDisabled = false;
+        policy.mSecondaryLockscreenEnabled = false;
         policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
         policy.mAffiliationIds.clear();
         policy.mLockTaskPackages.clear();
@@ -11154,6 +11168,33 @@
     }
 
     @Override
+    public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled) {
+        enforceCanSetSecondaryLockscreenEnabled(who);
+        synchronized (getLockObject()) {
+            final int userId = mInjector.userHandleGetCallingUserId();
+            DevicePolicyData policy = getUserData(userId);
+            policy.mSecondaryLockscreenEnabled = enabled;
+            saveSettingsLocked(userId);
+        }
+    }
+
+    @Override
+    public boolean isSecondaryLockscreenEnabled(int userId) {
+        synchronized (getLockObject()) {
+            return getUserData(userId).mSecondaryLockscreenEnabled;
+        }
+    }
+
+    private void enforceCanSetSecondaryLockscreenEnabled(ComponentName who) {
+        enforceProfileOrDeviceOwner(who);
+        final int userId = mInjector.userHandleGetCallingUserId();
+        if (isManagedProfile(userId)) {
+            throw new SecurityException(
+                    "User " + userId + " is not allowed to call setSecondaryLockscreenEnabled");
+        }
+    }
+
+    @Override
     public void setLockTaskPackages(ComponentName who, String[] packages)
             throws SecurityException {
         Objects.requireNonNull(who, "ComponentName is null");
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index aeba488..8f1d0f7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4189,6 +4189,52 @@
                 () -> dpm.setLockTaskFeatures(admin1, flags));
     }
 
+    public void testSecondaryLockscreen_profileOwner() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+        // Initial state is disabled.
+        assertFalse(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Profile owner can set enabled state.
+        setAsProfileOwner(admin1);
+        dpm.setSecondaryLockscreenEnabled(admin1, true);
+        assertTrue(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Managed profile managed by different package is unaffiliated - cannot set enabled.
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 20456);
+        final ComponentName adminDifferentPackage =
+                new ComponentName("another.package", "whatever.class");
+        addManagedProfile(adminDifferentPackage, managedProfileAdminUid, admin2);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setSecondaryLockscreenEnabled(adminDifferentPackage, false));
+    }
+
+    public void testSecondaryLockscreen_deviceOwner() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        // Initial state is disabled.
+        assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.USER_SYSTEM));
+
+        // Device owners can set enabled state.
+        setupDeviceOwner();
+        dpm.setSecondaryLockscreenEnabled(admin1, true);
+        assertTrue(dpm.isSecondaryLockscreenEnabled(UserHandle.USER_SYSTEM));
+    }
+
+    public void testSecondaryLockscreen_nonOwner() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+        // Initial state is disabled.
+        assertFalse(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Non-DO/PO cannot set enabled state.
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setSecondaryLockscreenEnabled(admin1, true));
+        assertFalse(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
     public void testIsDeviceManaged() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();