Adding unlockProfile to trust-agent meta data and makes TrustAgent runnable under direct boot

Test: manual
Change-Id: I1f0ccafac08be2cc3f4540232e764ef21d4fed38
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2d61e6e..d03f2e7 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7442,6 +7442,8 @@
         <attr name="title" />
         <!-- @SystemApi Summary for the same preference as the title. @hide -->
         <attr name="summary" />
+        <!-- @SystemApi Whether trust agent can unlock a user profile @hide -->
+        <attr name="unlockProfile" format="boolean"/>
     </declare-styleable>
 
     <!-- =============================== -->
@@ -8312,13 +8314,13 @@
         <attr name="color" />
     </declare-styleable>
 
-    <!-- @hide Attributes which will be read by the Activity to intialize the 
+    <!-- @hide Attributes which will be read by the Activity to intialize the
                base activity TaskDescription. -->
     <declare-styleable name="ActivityTaskDescription">
         <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary 
                    color. -->
         <attr name="colorPrimary" />
-        <!-- @hide From Theme.colorBackground, used for the TaskDescription background 
+        <!-- @hide From Theme.colorBackground, used for the TaskDescription background
                    color. -->
         <attr name="colorBackground" />
     </declare-styleable>
diff --git a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
index a7c9105..edcea0e 100644
--- a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
@@ -24,6 +24,7 @@
           android:name=".SampleTrustAgent"
           android:label="@string/app_name"
           android:permission="android.permission.BIND_TRUST_AGENT"
+          android:directBootAware="true"
           android:exported="true">
         <intent-filter>
           <action android:name="android.service.trust.TrustAgentService" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/layout-v26/sample_trust_agent_settings.xml b/packages/Keyguard/test/SampleTrustAgent/res/layout-v26/sample_trust_agent_settings.xml
new file mode 100644
index 0000000..4669971
--- /dev/null
+++ b/packages/Keyguard/test/SampleTrustAgent/res/layout-v26/sample_trust_agent_settings.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    <Button android:id="@+id/enable_trust"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Grant trust for 30 seconds" />
+    <Button android:id="@+id/revoke_trust"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Revoke trust" />
+    <Button android:id="@+id/crash"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Crash" />
+    <CheckBox android:id="@+id/managing_trust"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Managing trust" />
+    <CheckBox android:id="@+id/managing_trust_direct_boot"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Managing trust direct boot"/>
+
+    <CheckBox android:id="@+id/report_unlock_attempts"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Report unlock attempts" />
+    <CheckBox android:id="@+id/report_device_locked"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="Report device locked or unlocked" />
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+        <Button android:id="@+id/check_device_locked"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Device locked?" />
+        <TextView android:id="@+id/check_device_locked_result"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/xml-v26/sample_trust_agent.xml b/packages/Keyguard/test/SampleTrustAgent/res/xml-v26/sample_trust_agent.xml
new file mode 100644
index 0000000..26d5aa0
--- /dev/null
+++ b/packages/Keyguard/test/SampleTrustAgent/res/xml-v26/sample_trust_agent.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+<trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
+             android:settingsActivity=".SampleTrustAgentSettings"
+             android:unlockProfile="true" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml b/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml
index b363ab4..6cd34bb 100644
--- a/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml
@@ -15,4 +15,4 @@
   ~ limitations under the License
   -->
 <trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
-        android:settingsActivity=".SampleTrustAgentSettings" />
+             android:settingsActivity=".SampleTrustAgentSettings" />
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index b8f16e7..4b50cf8 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.os.PersistableBundle;
+import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.service.trust.TrustAgentService;
 import android.support.v4.content.LocalBroadcastManager;
@@ -57,26 +58,47 @@
             = "preference.report_unlock_attempts";
     private static final String PREFERENCE_MANAGING_TRUST
             = "preference.managing_trust";
+    private static final String PREFERENCE_MANAGING_TRUST_DIRECT_BOOT
+            = "preference.managing_trust_direct_boot";
     private static final String PREFERENCE_REPORT_DEVICE_LOCKED = "preference.report_device_locked";
 
     private static final String TAG = "SampleTrustAgent";
 
+    private static final BroadcastReceiver mUnlockReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+        }
+    };
+
+    private boolean mIsUserUnlocked;
+
     @Override
     public void onCreate() {
         super.onCreate();
+        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+        mIsUserUnlocked = um.isUserUnlocked();
+        Log.i(TAG,, "onCreate, is user unlocked=" + mIsUserUnlocked);
         mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_GRANT_TRUST);
         filter.addAction(ACTION_REVOKE_TRUST);
+        if (!mIsUserUnlocked) {
+            filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+        }
         mLocalBroadcastManager.registerReceiver(mReceiver, filter);
         if (ALLOW_EXTERNAL_BROADCASTS) {
             registerReceiver(mReceiver, filter);
         }
 
-        setManagingTrust(getIsManagingTrust(this));
-        PreferenceManager.getDefaultSharedPreferences(this)
-                .registerOnSharedPreferenceChangeListener(this);
+        if (!mIsUserUnlocked) {
+            boolean trustManaged = getIsManagingTrustDirectBoot(this);
+            Log.i(TAG, "in Direct boot." + (trustManaged ? "manage" : "cannot manage") + "trust");
+            setManagingTrust(getIsManagingTrustDirectBoot(this));
+        } else {
+            onBootCompleted();
+        }
     }
 
     @Override
@@ -137,6 +159,12 @@
                 .unregisterOnSharedPreferenceChangeListener(this);
     }
 
+    private void onBootCompleted() {
+        PreferenceManager.getDefaultSharedPreferences(this)
+                .registerOnSharedPreferenceChangeListener(this);
+        setManagingTrust(getIsManagingTrust(this));
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -158,6 +186,9 @@
                 }
             } else if (ACTION_REVOKE_TRUST.equals(action)) {
                 revokeTrust();
+            } else if (intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                Log.d(TAG, "User unlocked and boot completed.");
+                onBootCompleted();
             }
         }
     };
@@ -203,6 +234,7 @@
     public static void setIsManagingTrust(Context context, boolean enabled) {
         SharedPreferences sharedPreferences = PreferenceManager
                 .getDefaultSharedPreferences(context);
+        Log.d("AAAA", "save manage trust preference. Enabled=" + enabled);
         sharedPreferences.edit().putBoolean(PREFERENCE_MANAGING_TRUST, enabled).apply();
     }
 
@@ -212,6 +244,21 @@
         return sharedPreferences.getBoolean(PREFERENCE_MANAGING_TRUST, false);
     }
 
+    public static void setIsManagingTrustDirectBoot(Context context, boolean enabled) {
+        Context directBootContext = context.createDeviceProtectedStorageContext();
+        SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(directBootContext);
+        Log.d("AAAA", "save to direct boot preference. Enabled=" + enabled);
+        sharedPreferences.edit().putBoolean(PREFERENCE_MANAGING_TRUST_DIRECT_BOOT, enabled).apply();
+    }
+
+    public static boolean getIsManagingTrustDirectBoot(Context context) {
+        Context directBootContext = context.createDeviceProtectedStorageContext();
+        SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(directBootContext);
+        return sharedPreferences.getBoolean(PREFERENCE_MANAGING_TRUST_DIRECT_BOOT, false);
+    }
+
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
         if (PREFERENCE_MANAGING_TRUST.equals(key)) {
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
index 29b15cb..1b17169 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java
@@ -33,6 +33,7 @@
     private CheckBox mReportUnlockAttempts;
     private CheckBox mReportDeviceLocked;
     private CheckBox mManagingTrust;
+    private CheckBox mManagingTrustDirectBoot;
     private TextView mCheckDeviceLockedResult;
 
     private KeyguardManager mKeyguardManager;
@@ -59,6 +60,8 @@
 
         mManagingTrust = (CheckBox) findViewById(R.id.managing_trust);
         mManagingTrust.setOnCheckedChangeListener(this);
+        mManagingTrustDirectBoot = (CheckBox) findViewById(R.id.managing_trust_direct_boot);
+        mManagingTrustDirectBoot.setOnCheckedChangeListener(this);
 
         mCheckDeviceLockedResult = (TextView) findViewById(R.id.check_device_locked_result);
     }
@@ -68,6 +71,8 @@
         super.onResume();
         mReportUnlockAttempts.setChecked(SampleTrustAgent.getReportUnlockAttempts(this));
         mManagingTrust.setChecked(SampleTrustAgent.getIsManagingTrust(this));
+        mManagingTrustDirectBoot.setChecked(
+            SampleTrustAgent.getIsManagingTrustDirectBoot(this));
         updateTrustedState();
     }
 
@@ -94,6 +99,8 @@
             SampleTrustAgent.setIsManagingTrust(this, isChecked);
         } else if (buttonView == mReportDeviceLocked) {
             SampleTrustAgent.setReportDeviceLocked(this, isChecked);
+        } else if (buttonView == mManagingTrustDirectBoot) {
+            SampleTrustAgent.setIsManagingTrustDirectBoot(this, isChecked);
         }
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9d02940..cca8cc8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -169,7 +169,7 @@
         CharSequence label;
         Drawable icon;
         ComponentName component; // service that implements ITrustAgent
-        ComponentName settings; // setting to launch to modify agent.
+        SettingsAttrs settings; // setting to launch to modify agent.
         TrustAgentWrapper agent;
         int userId;
 
@@ -258,11 +258,6 @@
                         + ": switchToByUser=false");
                 continue;
             }
-            if (!StorageManager.isUserKeyUnlocked(userInfo.id)) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": FDE still locked");
-                continue;
-            }
             if (!mActivityManager.isUserRunning(userInfo.id)) {
                 if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
                         + ": user not started");
@@ -273,13 +268,7 @@
                         + ": no secure credential");
                 continue;
             }
-            if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": prevented by StrongAuthTracker = 0x"
-                        + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
-                        userInfo.id)));
-                continue;
-            }
+
             DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
             int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
             final boolean disableTrustAgents =
@@ -312,16 +301,49 @@
                         continue;
                     }
                 }
-
                 AgentInfo agentInfo = new AgentInfo();
                 agentInfo.component = name;
                 agentInfo.userId = userInfo.id;
                 if (!mActiveAgents.contains(agentInfo)) {
                     agentInfo.label = resolveInfo.loadLabel(pm);
                     agentInfo.icon = resolveInfo.loadIcon(pm);
-                    agentInfo.settings = getSettingsComponentName(pm, resolveInfo);
+                    agentInfo.settings = getSettingsAttrs(pm, resolveInfo);
                     agentInfo.agent = new TrustAgentWrapper(mContext, this,
                             new Intent().setComponent(name), userInfo.getUserHandle());
+                } else {
+                    int index = mActiveAgents.indexOf(agentInfo);
+                    agentInfo = mActiveAgents.valueAt(index);
+                }
+
+                boolean directUnlock = resolveInfo.serviceInfo.directBootAware
+                    && agentInfo.settings.canUnlockProfile;
+
+                if (directUnlock) {
+                    if (DEBUG) Slog.d(TAG, "refreshAgentList: trustagent " + name
+                            + "of user " + userInfo.id + "can unlock user profile.");
+                }
+
+                if (!StorageManager.isUserKeyUnlocked(userInfo.id)
+                        && !directUnlock) {
+                    if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+                            + "'s trust agent " + name + ": FDE still locked and "
+                            + " the agent cannot unlock user profile.");
+                    continue;
+                }
+
+                if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) {
+                    int flag = mStrongAuthTracker.getStrongAuthForUser(userInfo.id);
+                    if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+                        || !directUnlock) {
+                        if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+                            + ": prevented by StrongAuthTracker = 0x"
+                            + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
+                            userInfo.id)));
+                        continue;
+                    }
+                }
+
+                if (!mActiveAgents.contains(agentInfo)) {
                     mActiveAgents.add(agentInfo);
                 } else {
                     obsoleteAgents.remove(agentInfo);
@@ -468,10 +490,12 @@
         refreshAgentList(userId);
     }
 
-    private ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
+    private SettingsAttrs getSettingsAttrs(PackageManager pm, ResolveInfo resolveInfo) {
         if (resolveInfo == null || resolveInfo.serviceInfo == null
                 || resolveInfo.serviceInfo.metaData == null) return null;
         String cn = null;
+        boolean canUnlockProfile = false;
+
         XmlResourceParser parser = null;
         Exception caughtException = null;
         try {
@@ -496,6 +520,8 @@
             TypedArray sa = res
                     .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
             cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
+            canUnlockProfile = sa.getBoolean(
+                    com.android.internal.R.styleable.TrustAgent_unlockProfile, false);
             sa.recycle();
         } catch (PackageManager.NameNotFoundException e) {
             caughtException = e;
@@ -516,7 +542,7 @@
         if (cn.indexOf('/') < 0) {
             cn = resolveInfo.serviceInfo.packageName + "/" + cn;
         }
-        return ComponentName.unflattenFromString(cn);
+        return new SettingsAttrs(ComponentName.unflattenFromString(cn), canUnlockProfile);
     }
 
     private ComponentName getComponentName(ResolveInfo resolveInfo) {
@@ -554,6 +580,7 @@
 
     private List<ResolveInfo> resolveAllowedTrustAgents(PackageManager pm, int userId) {
         List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
+                PackageManager.GET_META_DATA |
                 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                 userId);
         ArrayList<ResolveInfo> allowedAgents = new ArrayList<>(resolveInfos.size());
@@ -991,6 +1018,18 @@
         }
     };
 
+    private static class SettingsAttrs {
+        public ComponentName componentName;
+        public boolean canUnlockProfile;
+
+        public SettingsAttrs(
+                ComponentName componentName,
+                boolean canUnlockProfile) {
+            this.componentName = componentName;
+            this.canUnlockProfile = canUnlockProfile;
+        }
+    };
+
     private class Receiver extends BroadcastReceiver {
 
         @Override