Enable outgoing emergency calls in LockTask mode.

As part of the LockTask 2.0 work, we allow DPCs to enable the system lock
screen in LockTask mode. When the device is protected by a PIN, pattern, or
password, the lock screen will have an entry point to the emergency dialer
which allows the user to make emergency calls (e.g., 911).

Therefore, we whitelist the activities that are necessary to place this
outgoing emergency call when LOCK_TASK_FEATURE_KEYGUARD is enabled.

Bug: 68750910
Test: bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
Test: CTS verifier > CTS verifier > Managed provisioning > Device owner tests
      > LockTask UI
Change-Id: Iaeeec2c462b978d2d201c5660024a3dd7283ae07
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index d77e1a2..ba3e25a 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -22,8 +22,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
 import static android.content.Context.STATUS_BAR_SERVICE;
+import static android.content.Intent.ACTION_CALL_EMERGENCY;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_CURRENT;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
@@ -45,6 +47,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.Handler;
@@ -52,9 +55,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.telecom.TelecomManager;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
@@ -133,6 +138,8 @@
     WindowManagerService mWindowManager;
     @VisibleForTesting
     LockPatternUtils mLockPatternUtils;
+    @VisibleForTesting
+    TelecomManager mTelecomManager;
 
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -165,7 +172,7 @@
     /**
      * Features that are allowed by DPC to show during LockTask mode.
      */
-    private final SparseArray<Integer> mLockTaskFeatures = new SparseArray<>();
+    private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
 
     /**
      * Store the current lock task mode. Possible values:
@@ -298,6 +305,11 @@
             return false;
         }
 
+        // Allow emergency calling when the device is protected by a locked keyguard
+        if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
+            return false;
+        }
+
         return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
     }
 
@@ -306,6 +318,37 @@
                 & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
     }
 
+    private boolean isKeyguardAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
+    }
+
+    private boolean isEmergencyCallTask(TaskRecord task) {
+        final Intent intent = task.intent;
+        if (intent == null) {
+            return false;
+        }
+
+        // 1. The emergency keypad activity launched on top of the keyguard
+        if (EMERGENCY_DIALER_COMPONENT.equals(intent.getComponent())) {
+            return true;
+        }
+
+        // 2. The intent sent by the keypad, which is handled by Telephony
+        if (ACTION_CALL_EMERGENCY.equals(intent.getAction())) {
+            return true;
+        }
+
+        // 3. Telephony then starts the default package for making the call
+        final TelecomManager tm = getTelecomManager();
+        final String dialerPackage = tm != null ? tm.getSystemDialerPackage() : null;
+        if (dialerPackage != null && dialerPackage.equals(intent.getComponent().getPackageName())) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Stop the current lock task mode.
      *
@@ -686,11 +729,10 @@
             mWindowManager.reenableKeyguard(mToken);
 
         } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
-            int lockTaskFeatures = getLockTaskFeaturesForUser(userId);
-            if ((DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD & lockTaskFeatures) == 0) {
-                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
-            } else {
+            if (isKeyguardAllowed(userId)) {
                 mWindowManager.reenableKeyguard(mToken);
+            } else {
+                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
             }
 
         } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
@@ -784,6 +826,15 @@
         return mLockPatternUtils;
     }
 
+    @Nullable
+    private TelecomManager getTelecomManager() {
+        if (mTelecomManager == null) {
+            // We don't preserve the TelecomManager object to save memory
+            return mContext.getSystemService(TelecomManager.class);
+        }
+        return mTelecomManager;
+    }
+
     // Should only be called on the handler thread
     @NonNull
     private LockTaskNotify getLockTaskNotify() {
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 54df744..d2ae22b 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -30,6 +30,7 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.os.Process.SYSTEM_UID;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
@@ -53,6 +54,7 @@
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.telecom.TelecomManager;
 import android.util.Pair;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -90,6 +92,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private LockTaskNotify mLockTaskNotify;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private TelecomManager mTelecomManager;
     @Mock private RecentTasks mRecentTasks;
 
     private LockTaskController mLockTaskController;
@@ -118,6 +121,7 @@
         mLockTaskController.setWindowManager(mWindowManager);
         mLockTaskController.mStatusBarService = mStatusBarService;
         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
+        mLockTaskController.mTelecomManager = mTelecomManager;
         mLockTaskController.mLockPatternUtils = mLockPatternUtils;
         mLockTaskController.mLockTaskNotify = mLockTaskNotify;
 
@@ -209,7 +213,7 @@
 
     @Test
     public void testLockTaskViolation() throws Exception {
-        // GIVEN one task records with whitelisted auth that is in lock task mode
+        // GIVEN one task record with whitelisted auth that is in lock task mode
         TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
@@ -234,6 +238,38 @@
     }
 
     @Test
+    public void testLockTaskViolation_emergencyCall() throws Exception {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // GIVEN tasks necessary for emergency calling
+        TaskRecord keypad = getTaskRecord(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord callAction = getTaskRecord(new Intent(Intent.ACTION_CALL_EMERGENCY),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord dialer = getTaskRecord("com.example.dialer", TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        when(mTelecomManager.getSystemDialerPackage())
+                .thenReturn(dialer.intent.getComponent().getPackageName());
+
+        // GIVEN keyguard is allowed for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
+
+        // THEN the above tasks should all be allowed
+        assertFalse(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(dialer));
+
+        // GIVEN keyguard is disallowed for lock task mode (default)
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
+
+        // THEN the above tasks should all be blocked
+        assertTrue(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(dialer));
+    }
+
+    @Test
     public void testStopLockTaskMode() throws Exception {
         // GIVEN one task record with whitelisted auth that is in lock task mode
         TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
@@ -568,10 +604,15 @@
     }
 
     private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) {
+        final Intent intent = new Intent()
+                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        return getTaskRecord(intent, lockTaskAuth);
+    }
+
+    private TaskRecord getTaskRecord(Intent intent, int lockTaskAuth) {
         TaskRecord tr = mock(TaskRecord.class);
         tr.mLockTaskAuth = lockTaskAuth;
-        tr.intent = new Intent()
-                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        tr.intent = intent;
         tr.userId = TEST_USER_ID;
         return tr;
     }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 92d458f..6dcc3da 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -582,13 +582,21 @@
             "android.telecom.extra.CALL_BACK_INTENT";
 
     /**
+     * The dialer activity responsible for placing emergency calls from, for example, a locked
+     * keyguard.
+     * @hide
+     */
+    public static final ComponentName EMERGENCY_DIALER_COMPONENT =
+            ComponentName.createRelative("com.android.phone", ".EmergencyDialer");
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
 
     /**
      * Indicates that the address or number of a call is allowed to be displayed for caller ID.
-    */
+     */
     public static final int PRESENTATION_ALLOWED = 1;
 
     /**