Support FBE for managed profiles.

Allow launcher to see and attempt to launch non-crypto
aware application when profile is locked.
Hide unlock notification until parent user is unlocked.
Have unlock notitication use confirm credentials to unlock
the profile.
Updated notification strings as per suggestions in mocks
to make it clearer between users and profiles.

Bug: 27038260

Change-Id: If2d2c8148670d814544f4edd44193d15da32a289
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4dbb490..c318140 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -34,6 +35,7 @@
 import android.content.res.Resources;
 
 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.Context.USER_SERVICE;
 import static android.Manifest.permission.READ_CONTACTS;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -124,7 +126,7 @@
         @Override
         public void onBootPhase(int phase) {
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-                mLockSettingsService.maybeShowEncryptionNotification(UserHandle.ALL);
+                mLockSettingsService.maybeShowEncryptionNotifications();
             } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                 // TODO
             }
@@ -176,22 +178,48 @@
      * If the account is credential-encrypted, show notification requesting the user to unlock
      * the device.
      */
-    private void maybeShowEncryptionNotification(UserHandle userHandle) {
-        if (UserHandle.ALL.equals(userHandle)) {
-            final List<UserInfo> users = mUserManager.getUsers();
-            for (int i = 0; i < users.size(); i++) {
-                UserHandle user = users.get(i).getUserHandle();
-                if (!mUserManager.isUserUnlocked(user)) {
-                    showEncryptionNotification(user);
+    private void maybeShowEncryptionNotifications() {
+        final List<UserInfo> users = mUserManager.getUsers();
+        for (int i = 0; i < users.size(); i++) {
+            UserInfo user = users.get(i);
+            UserHandle userHandle = user.getUserHandle();
+            if (!mUserManager.isUserUnlocked(userHandle)) {
+                if (!user.isManagedProfile()) {
+                    showEncryptionNotification(userHandle);
+                } else {
+                    UserInfo parent = mUserManager.getProfileParent(user.id);
+                    if (parent != null && mUserManager.isUserUnlocked(parent.getUserHandle())) {
+                        // Only show notifications for managed profiles once their parent
+                        // user is unlocked.
+                        showEncryptionNotificationForProfile(userHandle);
+                    }
                 }
             }
-        } else if (!mUserManager.isUserUnlocked(userHandle)){
-            showEncryptionNotification(userHandle);
         }
     }
 
+    private void showEncryptionNotificationForProfile(UserHandle user) {
+        Resources r = mContext.getResources();
+        CharSequence title = r.getText(
+                com.android.internal.R.string.user_encrypted_title);
+        CharSequence message = r.getText(
+                com.android.internal.R.string.profile_encrypted_message);
+        CharSequence detail = r.getText(
+                com.android.internal.R.string.profile_encrypted_detail);
+
+        final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
+        final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null, user.getIdentifier());
+        if (unlockIntent == null) {
+            return;
+        }
+        unlockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        showEncryptionNotification(user, title, message, detail, intent);
+    }
+
     private void showEncryptionNotification(UserHandle user) {
-        if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
         Resources r = mContext.getResources();
         CharSequence title = r.getText(
                 com.android.internal.R.string.user_encrypted_title);
@@ -203,6 +231,12 @@
         PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
                 PendingIntent.FLAG_UPDATE_CURRENT);
 
+        showEncryptionNotification(user, title, message, detail, intent);
+    }
+
+    private void showEncryptionNotification(UserHandle user, CharSequence title, CharSequence message,
+            CharSequence detail, PendingIntent intent) {
+        if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
         Notification notification = new Notification.Builder(mContext)
                 .setSmallIcon(com.android.internal.R.drawable.ic_user_secure)
                 .setWhen(0)
@@ -230,8 +264,21 @@
         hideEncryptionNotification(new UserHandle(userId));
     }
 
-    public void onUnlockUser(int userHandle) {
-        hideEncryptionNotification(new UserHandle(userHandle));
+    public void onUnlockUser(int userId) {
+        hideEncryptionNotification(new UserHandle(userId));
+
+        // Now we have unlocked the parent user we should show notifications
+        // about any profiles that exist.
+        List<UserInfo> profiles = mUserManager.getProfiles(userId);
+        for (int i = 0; i < profiles.size(); i++) {
+            UserInfo profile = profiles.get(i);
+            if (profile.isManagedProfile()) {
+                UserHandle userHandle = profile.getUserHandle();
+                if (!mUserManager.isUserUnlocked(userHandle)) {
+                    showEncryptionNotificationForProfile(userHandle);
+                }
+            }
+        }
     }
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e90b5db..93a36eb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1046,10 +1046,14 @@
     }
 
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+        return resolveIntent(intent, resolvedType, userId, 0);
+    }
+
+    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
         try {
             return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
-                            PackageManager.MATCH_DEFAULT_ONLY
-                            | ActivityManagerService.STOCK_PM_FLAGS, userId);
+                    PackageManager.MATCH_DEFAULT_ONLY | flags
+                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
         } catch (RemoteException e) {
         }
         return null;
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1ed749f..9b2bca0 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 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.server.am;
 
 import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 28be456..cca6fc5 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 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.server.am;
 
 import static android.app.Activity.RESULT_CANCELED;
@@ -74,7 +90,9 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -84,6 +102,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
 import android.util.Slog;
@@ -582,6 +601,22 @@
         intent = new Intent(intent);
 
         ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
+        if (rInfo == null) {
+            UserInfo userInfo = mSupervisor.getUserInfo(userId);
+            if (userInfo != null && userInfo.isManagedProfile()) {
+                // Special case for managed profiles, if attempting to launch non-cryto aware
+                // app in a locked managed profile from an unlocked parent allow it to resolve
+                // as user will be sent via confirm credentials to unlock the profile.
+                UserManager userManager = UserManager.get(mService.mContext);
+                UserInfo parent = userManager.getProfileParent(userId);
+                if (parent != null
+                        && userManager.isUserUnlocked(parent.getUserHandle())
+                        && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
+                            PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+                }
+            }
+        }
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a355fa4..1c18a2f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -297,6 +297,22 @@
                         null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                         userId);
 
+                if (getUserInfo(userId).isManagedProfile()) {
+                    UserInfo parent = getUserManager().getProfileParent(userId);
+                    if (parent != null) {
+                        final Intent profileUnlockedIntent = new Intent(
+                                Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+                        unlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+                        unlockedIntent.addFlags(
+                                Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                                | Intent.FLAG_RECEIVER_FOREGROUND);
+                        mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+                                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                                null, false, false, MY_PID, SYSTEM_UID,
+                                parent.id);
+                    }
+                }
+
                 final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                 bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index d82bb3d..c6613f5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -201,8 +201,7 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                        user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return new ParceledListSlice<>(apps);
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -220,7 +219,7 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 ResolveInfo app = mPm.resolveActivityAsUser(intent,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return app;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -239,7 +238,7 @@
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
                 PackageInfo info = pm.getPackageInfo(packageName,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return info != null && info.applicationInfo.enabled;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -277,7 +276,7 @@
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
                 ActivityInfo info = pm.getActivityInfo(component,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 return info != null;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -303,7 +302,7 @@
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
                 ActivityInfo info = pm.getActivityInfo(component,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 if (!info.exported) {
                     throw new SecurityException("Cannot launch non-exported components "
                             + component);
@@ -313,7 +312,7 @@
                 // as calling startActivityAsUser ignores the category and just
                 // resolves based on the component if present.
                 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, user.getIdentifier());
                 final int size = apps.size();
                 for (int i = 0; i < size; ++i) {
                     ActivityInfo activityInfo = apps.get(i).activityInfo;