DevicePolicyManager Authentication for Lock Task

Here we let DevicePolicyManager keep a list of tasks that are
allowed to start the lock task mode. This list can only be set by
a device owner app. The ActivityManager will call
DevicePolicyManager to check whether a given task can start the
lock task mode or not.

Change-Id: I650fdae43fc35bf9fd63452283f4e2bbadd11551
Bug: 14611303
diff --git a/api/current.txt b/api/current.txt
index 302e77e..546e71b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5056,6 +5056,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isDeviceOwnerApp(java.lang.String);
+    method public boolean isLockTaskPermitted(android.content.ComponentName);
     method public boolean isProfileOwnerApp(java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
@@ -5064,6 +5065,7 @@
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
+    method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
     method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 61ff60a..24bb2cc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2125,4 +2125,51 @@
 
         return null;
     }
+
+    /**
+     * Sets which components may enter lock task mode.
+     *
+     * This function can only be called by the device owner or the profile owner.
+     * @param components The list of components allowed to enter lock task mode
+     */
+    public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+        if (mService != null) {
+            try {
+                mService.setLockTaskComponents(components);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * This function returns the list of components allowed to start the lock task mode.
+     * @hide
+     */
+    public ComponentName[] getLockTaskComponents() {
+        if (mService != null) {
+            try {
+                return mService.getLockTaskComponents();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * This function lets the caller know whether the given component is allowed to start the
+     * lock task mode.
+     * @param component The component to check
+     */
+    public boolean isLockTaskPermitted(ComponentName component) {
+        if (mService != null) {
+            try {
+                return mService.isLockTaskPermitted(component);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0096580..03ced0f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -129,4 +129,8 @@
 
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
+
+    void setLockTaskComponents(in ComponentName[] components);
+    ComponentName[] getLockTaskComponents();
+    boolean isLockTaskPermitted(in ComponentName component);
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9a9f1c8..4e22b2a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -97,6 +97,7 @@
 import java.security.cert.X509Certificate;
 import java.text.DateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -113,6 +114,8 @@
 
     private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
+    private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = 86400 * 1000;
@@ -182,6 +185,9 @@
         final ArrayList<ActiveAdmin> mAdminList
                 = new ArrayList<ActiveAdmin>();
 
+        // This is the list of component allowed to start lock task mode.
+        final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -955,6 +961,13 @@
                 out.endTag(null, "active-password");
             }
 
+            for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
+                ComponentName component = policy.mLockTaskComponents.get(i);
+                out.startTag(null, LOCK_TASK_COMPONENTS_XML);
+                out.attribute(null, "name", component.flattenToString());
+                out.endTag(null, LOCK_TASK_COMPONENTS_XML);
+            }
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -1004,6 +1017,7 @@
             }
             type = parser.next();
             int outerDepth = parser.getDepth();
+            policy.mLockTaskComponents.clear();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1056,6 +1070,11 @@
                     policy.mActivePasswordNonLetter = Integer.parseInt(
                             parser.getAttributeValue(null, "nonletter"));
                     XmlUtils.skipCurrentTag(parser);
+                } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
+                    policy.mLockTaskComponents.add
+                        (ComponentName.unflattenFromString
+                         (parser.getAttributeValue(null, "name")));
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -3344,4 +3363,74 @@
             return resultSet.toArray(new String[resultSet.size()]);
         }
     }
+
+    /**
+     * Sets which componets may enter lock task mode.
+     *
+     * This function can only be called by the device owner or the profile owner.
+     * @param components The list of components allowed to enter lock task mode.
+     */
+    public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+        // Get the package names of the caller.
+        int uid = Binder.getCallingUid();
+        String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+
+        // Check whether any of the package name is the device owner or the profile owner.
+        for (int i=0; i<packageNames.length; i++) {
+            String packageName = packageNames[i];
+            int userHandle = UserHandle.getUserId(uid);
+            String profileOwnerPackage = getProfileOwner(userHandle);
+            if (isDeviceOwner(packageName) ||
+                (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
+
+                // If a package name is the device owner or the profile owner,
+                // we update the component list.
+                DevicePolicyData policy = getUserData(userHandle);
+                policy.mLockTaskComponents.clear();
+                if (components != null) {
+                    for (int j=0; j<components.length; j++) {
+                        ComponentName component = components[j];
+                        policy.mLockTaskComponents.add(component);
+                    }
+                }
+
+                // Store the settings persistently.
+                saveSettingsLocked(userHandle);
+                return;
+            }
+        }
+        throw new SecurityException();
+    }
+
+    /**
+     * This function returns the list of components allowed to start the task lock mode.
+     */
+    public ComponentName[] getLockTaskComponents() {
+        int userHandle = UserHandle.USER_OWNER;
+        DevicePolicyData policy = getUserData(userHandle);
+        ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]);
+        return tempArray;
+    }
+
+    /**
+     * This function lets the caller know whether the given component is allowed to start the
+     * lock task mode.
+     * @param component The component to check
+     */
+    public boolean isLockTaskPermitted(ComponentName component) {
+        // Get current user's devicepolicy
+        int uid = Binder.getCallingUid();
+        int userHandle = UserHandle.getUserId(uid);
+        DevicePolicyData policy = getUserData(userHandle);
+        for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
+            ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i);
+
+            // If the given component equals one of the component stored our device-owner-set
+            // list, we allow this component to start the lock task mode.
+            if (lockTaskComponent.getPackageName().equals(component.getPackageName())) {
+                return true;
+            }
+        }
+        return false;
+    }
 }