From b1b302660cf5b1b1c5b342cc90eca93b8f66890c Mon Sep 17 00:00:00 2001 From: Kenny Guy Date: Tue, 9 Feb 2016 16:02:35 +0000 Subject: [PATCH] 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 --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + core/java/android/content/Intent.java | 11 +++ core/res/res/values/strings.xml | 5 ++ core/res/res/values/symbols.xml | 2 + .../android/server/LockSettingsService.java | 73 +++++++++++++++---- .../server/am/ActivityStackSupervisor.java | 8 +- .../server/am/ActivityStartInterceptor.java | 16 ++++ .../android/server/am/ActivityStarter.java | 35 +++++++++ .../com/android/server/am/UserController.java | 16 ++++ .../server/pm/LauncherAppsService.java | 13 ++-- 12 files changed, 160 insertions(+), 22 deletions(-) diff --git a/api/current.txt b/api/current.txt index 7f4075a24c3d0..b2facd6f89a75 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8512,6 +8512,7 @@ package android.content { field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; + field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED"; field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; diff --git a/api/system-current.txt b/api/system-current.txt index 75988ababdc91..9472d61811c14 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8818,6 +8818,7 @@ package android.content { field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; + field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED"; field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; diff --git a/api/test-current.txt b/api/test-current.txt index dfbea5f59d04b..93ac2857cf75e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8517,6 +8517,7 @@ package android.content { field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED"; field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; + field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED"; field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0f6f856ae12a9..b476a25515f5e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3027,6 +3027,17 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; + /** + * Broadcast sent to the primary user when the credential-encrypted private storage for + * an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that + * specifies the UserHandle of the profile that was unlocked. Only applications (for example + * Launchers) that need to display merged content across both primary and managed profiles + * need to worry about this broadcast. This is only sent to registered receivers, + * not manifest receivers. + */ + public static final String ACTION_MANAGED_PROFILE_UNLOCKED = + "android.intent.action.MANAGED_PROFILE_UNLOCKED"; + /** * Broadcast sent to the primary user when an associated managed profile's availability has * changed. This includes when the user toggles the profile's quiet mode. Carries an extra diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1717bb9df573e..daa8202fa3e59 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4210,6 +4210,11 @@ User data locked + + Work profile locked + + Tap to unlock work profile + Connected to %1$s diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f75f023b8c313..8df6c2e923acf 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2522,6 +2522,8 @@ + + diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 4dbb49005c28b..c318140ae7e8c 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.pm.UserInfo; 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 @@ public class LockSettingsService extends ILockSettings.Stub { @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 @@ public class LockSettingsService extends ILockSettings.Stub { * 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 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 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 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 e90b5db5ac4ed..93a36eb1d3d88 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } 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 1ed749fdd9da9..9b2bca009da66 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 28be456628ae8..cca6fc5a85d55 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.Intent; 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.IBinder; 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 @@ class ActivityStarter { 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 a355fa4750eb8..1c18a2fa952e8 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 @@ final class UserController { 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 d82bb3d1ed9fc..c6613f54d63cd 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 @@ public class LauncherAppsService extends SystemService { long ident = Binder.clearCallingIdentity(); try { List 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { // as calling startActivityAsUser ignores the category and just // resolves based on the component if present. List 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;