diff --git a/api/current.txt b/api/current.txt index 4b37c239ac7af..b81d3e723929d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8515,6 +8515,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 5167859e944fb..d67fc5dfe253a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8821,6 +8821,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 6407e64eb9bb8..8d9a126b52d44 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8520,6 +8520,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 b55212145b9a2..8e860fa524c58 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;