Introduced DO_NOT_ASK_CREDENTIALS_ON_BOOT flag

A new flag for DPM.resetPassword() method that specifies that the
device should be decrypted without asking for the password or pattern.

Bug 19250601

Related CL in Settings App: https://googleplex-android-review.git.corp.google.com/#/c/670206

Change-Id: I9ca3472dc18e66e618ff772dee16ca4a450e9997
diff --git a/api/current.txt b/api/current.txt
index 058053b..b594c5e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5801,6 +5801,7 @@
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+    field public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 24da8782..e1f4e3f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5907,6 +5907,7 @@
     field public static final java.lang.String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+    field public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 35b8a8ee..9e2da61 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1635,6 +1635,23 @@
     }
 
     /**
+     * Queries whether {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set.
+     *
+     * @return true if DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set.
+     * @hide
+     */
+    public boolean getDoNotAskCredentialsOnBoot() {
+        if (mService != null) {
+            try {
+                return mService.getDoNotAskCredentialsOnBoot();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to call getDoNotAskCredentialsOnBoot()", e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Setting this to a value greater than zero enables a built-in policy
      * that will perform a device wipe after too many incorrect
      * device-unlock passwords have been entered.  This built-in policy combines
@@ -1711,6 +1728,16 @@
     public static final int RESET_PASSWORD_REQUIRE_ENTRY = 0x0001;
 
     /**
+     * Flag for {@link #resetPassword}: don't ask for user credentials on device boot.
+     * If the flag is set, the device can be booted without asking for user password.
+     * The absence of this flag does not change the current boot requirements. This flag
+     * can be set by the device owner only. If the app is not the device owner, the flag
+     * is ignored. Once the flag is set, it cannot be reverted back without resetting the
+     * device to factory defaults.
+     */
+    public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002;
+
+    /**
      * Force a new device unlock password (the password needed to access the
      * entire device, not for individual accounts) on the user.  This takes
      * effect immediately.
@@ -1733,7 +1760,8 @@
      * <p>Calling this from a managed profile will throw a security exception.
      *
      * @param password The new password for the user. Null or empty clears the password.
-     * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}.
+     * @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and
+     *              {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT}.
      * @return Returns true if the password was applied, or false if it is
      * not acceptable for the current constraints.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 48ed45b..1f7498e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -224,4 +224,5 @@
 
     boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
     void setStatusBarEnabledState(in ComponentName who, boolean enabled);
+    boolean getDoNotAskCredentialsOnBoot();
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index aa60eba..1096e34 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -24,7 +24,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.os.AsyncTask;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -32,7 +31,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -544,8 +542,7 @@
             // Update the device encryption password.
             if (userId == UserHandle.USER_OWNER
                     && LockPatternUtils.isDeviceEncryptionEnabled()) {
-                final boolean required = isCredentialRequiredToDecrypt(true);
-                if (!required) {
+                if (!shouldEncryptWithCredentials(true)) {
                     clearEncryptionPassword();
                 } else {
                     String stringPattern = patternToString(pattern);
@@ -759,7 +756,7 @@
             // Update the device encryption password.
             if (userHandle == UserHandle.USER_OWNER
                     && LockPatternUtils.isDeviceEncryptionEnabled()) {
-                if (!isCredentialRequiredToDecrypt(true)) {
+                if (!shouldEncryptWithCredentials(true)) {
                     clearEncryptionPassword();
                 } else {
                     boolean numeric = computedQuality
@@ -1238,4 +1235,12 @@
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
     }
+
+    private boolean isDoNotAskCredentialsOnBootSet() {
+        return mDevicePolicyManager.getDoNotAskCredentialsOnBoot();
+    }
+
+    private boolean shouldEncryptWithCredentials(boolean defaultValue) {
+        return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet();
+    }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8f4d9a3..1f529ca 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2404,6 +2404,12 @@
         android:description="@string/permdesc_bindCarrierConfigService"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
+         flag is set.
+         @hide -->
+    <permission android:name="android.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT"
+                android:protectionLevel="signature" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7123cc0..6f554f08 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1302,7 +1302,6 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
 
-
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_use_sip">make/receive SIP calls</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bc0910e..c2e8ccc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -160,6 +160,9 @@
 
     private static final String ATTR_ENABLED = "enabled";
 
+    private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML =
+            "do-not-ask-credentials-on-boot";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = 86400 * 1000;
@@ -307,6 +310,8 @@
 
         String mDelegatedCertInstallerPackage;
 
+        boolean doNotAskCredentialsOnBoot = false;
+
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -1456,6 +1461,11 @@
                 out.endTag(null, TAG_STATUS_BAR);
             }
 
+            if (policy.doNotAskCredentialsOnBoot) {
+                out.startTag(null, DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML);
+                out.endTag(null, DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML);
+            }
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -1581,6 +1591,8 @@
                     policy.mStatusBarEnabledState = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_ENABLED));
                     XmlUtils.skipCurrentTag(parser);
+                } else if (DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML.equals(tag)) {
+                    policy.doNotAskCredentialsOnBoot = true;
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -2840,6 +2852,13 @@
             return false;
         }
 
+        boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwnerOrInitializer(callingUid);
+        boolean doNotAskCredentialsOnBoot =
+                (flags & DevicePolicyManager.DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
+        if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
+            setDoNotAskCredentialsOnBoot();
+        }
+
         // Don't do this with the lock held, because it is going to call
         // back in to the service.
         long ident = Binder.clearCallingIdentity();
@@ -2868,6 +2887,25 @@
         return true;
     }
 
+    private void setDoNotAskCredentialsOnBoot() {
+        synchronized (this) {
+            DevicePolicyData policyData = getUserData(UserHandle.USER_OWNER);
+            if (!policyData.doNotAskCredentialsOnBoot) {
+                policyData.doNotAskCredentialsOnBoot = true;
+                saveSettingsLocked(UserHandle.USER_OWNER);
+            }
+        }
+    }
+
+    public boolean getDoNotAskCredentialsOnBoot() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null);
+        synchronized (this) {
+            DevicePolicyData policyData = getUserData(UserHandle.USER_OWNER);
+            return policyData.doNotAskCredentialsOnBoot;
+        }
+    }
+
     public void setMaximumTimeToLock(ComponentName who, long timeMs) {
         if (!mHasFeature) {
             return;
@@ -6036,4 +6074,20 @@
             return mDeviceOwner.getSystemUpdatePolicy();
         }
     }
+
+    /**
+     * Checks if the caller of the method is the device owner app or device initialization app.
+     *
+     * @param callerUid UID of the caller.
+     * @return true if the caller is the device owner app or device initializer.
+     */
+    private boolean isCallerDeviceOwnerOrInitializer(int callerUid) {
+        String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
+        for (String pkg : pkgs) {
+            if (isDeviceOwner(pkg) || isDeviceInitializer(pkg)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }