From a1771110d67fa7361f92d92f2e91019882ce3305 Mon Sep 17 00:00:00 2001 From: Clara Bayarri Date: Fri, 18 Dec 2015 16:29:18 +0000 Subject: [PATCH] Create Work Challenge per-user condition Change the current static condition to a per-user condition so we can check and enable/disable the work challenge properly. Also add an isAllowed API, as the Work Challenge can only be used when the user's DPC targets N or above to maintain backwards compatibility. Change-Id: I0cb8b475838816801868ffb24726407aa257b4de --- .../app/admin/DevicePolicyManager.java | 16 ++++++ .../app/admin/IDevicePolicyManager.aidl | 2 + .../internal/widget/LockPatternUtils.java | 49 +++++++++++++++++-- .../keyguard/KeyguardViewMediator.java | 7 ++- .../android/server/LockSettingsService.java | 2 +- .../android/server/LockSettingsStorage.java | 4 +- .../com/android/server/am/UserController.java | 8 +-- .../android/server/pm/UserManagerService.java | 5 +- .../server/trust/TrustManagerService.java | 17 +++---- .../DevicePolicyManagerService.java | 37 ++++++++------ 10 files changed, 108 insertions(+), 39 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 4de3ceb4792e6..bd0635de39272 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -940,6 +940,22 @@ public class DevicePolicyManager { return false; } + /** + * Returns true if the Profile Challenge is available to use for the given profile user. + * + * @hide + */ + public boolean isSeparateProfileChallengeAllowed(int userHandle) { + if (mService != null) { + try { + return mService.isSeparateProfileChallengeAllowed(userHandle); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + /** * Constant for {@link #setPasswordQuality}: the policy has no requirements * for the password. Note that quality constants are ordered so that higher diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d3c32c51798a6..995ce0017ddc9 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -256,4 +256,6 @@ interface IDevicePolicyManager { String getShortSupportMessageForUser(in ComponentName admin, int userHandle); String getLongSupportMessageForUser(in ComponentName admin, int userHandle); + + boolean isSeparateProfileChallengeAllowed(int userHandle); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e38d82fdca390..89cb5b4a4adba 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -24,6 +24,7 @@ import android.app.trust.TrustManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.UserInfo; import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; @@ -34,6 +35,7 @@ import android.os.ServiceManager; 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; @@ -135,6 +137,8 @@ public class LockPatternUtils { private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; + private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge"; + // Maximum allowed number of repeated or ordered characters in a sequence before we'll // consider it a complex PIN/password. public static final int MAX_ALLOWED_SEQUENCE = 3; @@ -143,6 +147,7 @@ public class LockPatternUtils { private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; + private UserManager mUserManager; public static final class RequestThrottledException extends Exception { @@ -173,6 +178,13 @@ public class LockPatternUtils { return mDevicePolicyManager; } + private UserManager getUserManager() { + if (mUserManager == null) { + mUserManager = UserManager.get(mContext); + } + return mUserManager; + } + private TrustManager getTrustManager() { TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); if (trust == null) { @@ -865,6 +877,39 @@ public class LockPatternUtils { DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); } + /** + * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op + * for user handles that do not belong to a managed profile. + */ + public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled) { + UserInfo info = getUserManager().getUserInfo(userHandle); + if (info.isManagedProfile()) { + setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userHandle); + } + } + + /** + * Retrieves whether the Separate Profile Challenge is enabled for this {@param userHandle}. + */ + public boolean isSeparateProfileChallengeEnabled(int userHandle) { + UserInfo info = getUserManager().getUserInfo(userHandle); + if (!info.isManagedProfile()) { + return false; + } + return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userHandle); + } + + /** + * Retrieves whether the current DPM allows use of the Profile Challenge. + */ + public boolean isSeparateProfileChallengeAllowed(int userHandle) { + UserInfo info = getUserManager().getUserInfo(userHandle); + if (!info.isManagedProfile()) { + return false; + } + return getDevicePolicyManager().isSeparateProfileChallengeAllowed(userHandle); + } + /** * Deserialize a pattern. * @param string The pattern serialized with {@link #patternToString} @@ -1288,10 +1333,6 @@ public class LockPatternUtils { } } - public static boolean isSeparateWorkChallengeEnabled() { - return StorageManager.isFileBasedEncryptionEnabled(); - } - public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) { try { getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7556c6b33a83c..ed29a8f25a87b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -744,8 +744,7 @@ public class KeyguardViewMediator extends SystemUI { long timeout; - UserInfo user = UserManager.get(mContext).getUserInfo(userId); - if ((!user.isManagedProfile() && LockPatternUtils.isSeparateWorkChallengeEnabled()) + if ((mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) || policyTimeout <= 0) { timeout = lockAfterTimeout; } else { @@ -785,9 +784,9 @@ public class KeyguardViewMediator extends SystemUI { private void doKeyguardLaterLockedForChildProfiles() { UserManager um = UserManager.get(mContext); List profiles = um.getEnabledProfiles(UserHandle.myUserId()); - if (LockPatternUtils.isSeparateWorkChallengeEnabled() && profiles.size() > 1) { + if (profiles.size() > 1) { for (UserInfo info : profiles) { - if (info.id != UserHandle.myUserId() && info.isManagedProfile()) { + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) { long userTimeout = getLockTimeout(info.id); long userWhen = SystemClock.elapsedRealtime() + userTimeout; Intent lockIntent = new Intent(DELAYED_LOCK_PROFILE_ACTION); diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 1a21f5c75f0af..377d52fb1ac56 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -695,7 +695,7 @@ public class LockSettingsService extends ILockSettings.Stub { unlockUser(userId, token); UserInfo info = UserManager.get(mContext).getUserInfo(userId); - if (LockPatternUtils.isSeparateWorkChallengeEnabled() && info.isManagedProfile()) { + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { TrustManager trustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); trustManager.setDeviceLockedForUser(userId, false); diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java index 137fa27ecd455..816c791d47faa 100644 --- a/services/core/java/com/android/server/LockSettingsStorage.java +++ b/services/core/java/com/android/server/LockSettingsStorage.java @@ -71,6 +71,7 @@ class LockSettingsStorage { private final Object mFileWriteLock = new Object(); private int mStoredCredentialType; + private LockPatternUtils mLockPatternUtils; class CredentialHash { static final int TYPE_NONE = -1; @@ -100,6 +101,7 @@ class LockSettingsStorage { public LockSettingsStorage(Context context, Callback callback) { mContext = context; mOpenHelper = new DatabaseHelper(context, callback); + mLockPatternUtils = new LockPatternUtils(context); } public void writeKeyValue(String key, String value, int userId) { @@ -388,7 +390,7 @@ class LockSettingsStorage { private int getUserParentOrSelfId(int userId) { // Device supports per user encryption, so lock is applied to the given user. - if (LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { return userId; } // Device uses Block Based Encryption, and the parent user's lock is used for the whole diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 717285930f5bb..56757ca28c5b2 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -143,6 +143,8 @@ final class UserController { private volatile UserManagerService mUserManager; + private final LockPatternUtils mLockPatternUtils; + UserController(ActivityManagerService service) { mService = service; mHandler = mService.mHandler; @@ -150,6 +152,7 @@ final class UserController { final UserState uss = new UserState(UserHandle.SYSTEM); mStartedUsers.put(UserHandle.USER_SYSTEM, uss); mUserLru.add(UserHandle.USER_SYSTEM); + mLockPatternUtils = new LockPatternUtils(mService.mContext); updateStartedUserArrayLocked(); } @@ -1294,13 +1297,12 @@ final class UserController { * intercept activity launches for work apps when the Work Challenge is present. */ boolean shouldConfirmCredentials(int userId) { - final UserInfo user = getUserInfo(userId); - if (!user.isManagedProfile() || !LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { return false; } final KeyguardManager km = (KeyguardManager) mService.mContext .getSystemService(KEYGUARD_SERVICE); - return km.isDeviceLocked(user.id); + return km.isDeviceLocked(userId); } void dump(PrintWriter pw, boolean dumpAll) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3d614a35c13b4..ce6b3693c2d71 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -276,6 +276,8 @@ public class UserManagerService extends IUserManager.Stub { private final ArrayList mUserRestrictionsListeners = new ArrayList<>(); + private final LockPatternUtils mLockPatternUtils; + private static UserManagerService sInstance; public static UserManagerService getInstance() { @@ -320,6 +322,7 @@ public class UserManagerService extends IUserManager.Stub { } mLocalService = new LocalService(); LocalServices.addService(UserManagerInternal.class, mLocalService); + mLockPatternUtils = new LockPatternUtils(mContext); } void systemReady() { @@ -456,7 +459,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public int getCredentialOwnerProfile(int userHandle) { checkManageUsersPermission("get the credential owner"); - if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) { synchronized (mUsersLock) { UserInfo profileParent = getProfileParentLU(userHandle); if (profileParent != null) { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index d888c5686d61d..78e3f7f087795 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -290,14 +290,9 @@ public class TrustManagerService extends SystemService { } public void setDeviceLockedForUser(int userId, boolean locked) { - if (LockPatternUtils.isSeparateWorkChallengeEnabled()) { - UserInfo info = mUserManager.getUserInfo(userId); - if (info.isManagedProfile()) { - synchronized (mDeviceLockedForUser) { - mDeviceLockedForUser.put(userId, locked); - } - } else { - Log.wtf(TAG, "Requested to change lock state for non-profile user " + userId); + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { + synchronized (mDeviceLockedForUser) { + mDeviceLockedForUser.put(userId, locked); } } } @@ -669,7 +664,7 @@ public class TrustManagerService extends SystemService { public boolean isDeviceLocked(int userId) throws RemoteException { userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, false /* allowAll */, true /* requireFull */, "isDeviceLocked", null); - if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { userId = resolveProfileParent(userId); } @@ -680,13 +675,13 @@ public class TrustManagerService extends SystemService { public boolean isDeviceSecure(int userId) throws RemoteException { userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, false /* allowAll */, true /* requireFull */, "isDeviceSecure", null); - if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { userId = resolveProfileParent(userId); } long token = Binder.clearCallingIdentity(); try { - return new LockPatternUtils(mContext).isSecure(userId); + return mLockPatternUtils.isSecure(userId); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 380763abf6e33..c4d5c50ac8506 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -254,6 +254,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final IPackageManager mIPackageManager; final UserManager mUserManager; final UserManagerInternal mUserManagerInternal; + private final LockPatternUtils mLockPatternUtils; final LocalService mLocalService; @@ -1329,6 +1330,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager()); mLocalService = new LocalService(); + mLockPatternUtils = new LockPatternUtils(mContext); mHasFeature = mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN); @@ -2573,10 +2575,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private boolean isAdminApiLevelPreN(@NonNull ComponentName who, int userHandle) { + private boolean isAdminApiLevelMOrBelow(@NonNull ComponentName who, int userHandle) { DeviceAdminInfo adminInfo = findAdmin(who, userHandle, false); return adminInfo.getActivityInfo().applicationInfo.targetSdkVersion - < Build.VERSION_CODES.N; + <= Build.VERSION_CODES.M; + } + + @Override + public boolean isSeparateProfileChallengeAllowed(int userHandle) { + ComponentName profileOwner = getProfileOwner(userHandle); + return !isAdminApiLevelMOrBelow(profileOwner, userHandle); } @Override @@ -2618,7 +2626,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return admin != null ? admin.passwordQuality : mode; } - if (LockPatternUtils.isSeparateWorkChallengeEnabled() && !parent) { + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle) && !parent) { // If a Work Challenge is in use, only return its restrictions. DevicePolicyData policy = getUserDataUnchecked(userHandle); final int N = policy.mAdminList.size(); @@ -2638,7 +2646,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Only aggregate data for the parent profile plus the non-work challenge // enabled profiles. if (!(userInfo.isManagedProfile() - && LockPatternUtils.isSeparateWorkChallengeEnabled())) { + && mLockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id))) { DevicePolicyData policy = getUserDataUnchecked(userInfo.id); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { @@ -3224,8 +3232,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); ComponentName adminComponentName = admin.info.getComponent(); // TODO: Include the Admin sdk level check in LockPatternUtils check. - ComponentName who = !isAdminApiLevelPreN(adminComponentName, userHandle) - && LockPatternUtils.isSeparateWorkChallengeEnabled() + ComponentName who = !isAdminApiLevelMOrBelow(adminComponentName, userHandle) + && mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle) ? adminComponentName : null; if (policy.mActivePasswordQuality < getPasswordQuality(who, userHandle, parent) || policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) { @@ -4028,7 +4036,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } enforceFullCrossUsersPermission(userHandle); // Managed Profile password can only be changed when per user encryption is present. - if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) { enforceNotManagedProfile(userHandle, "set the active password"); } @@ -4055,7 +4063,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { setExpirationAlarmCheckLocked(mContext, policy); // Send a broadcast to each profile using this password as its primary unlock. - if (LockPatternUtils.isSeparateWorkChallengeEnabled()) { + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) { sendAdminCommandLocked( DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); @@ -4647,10 +4655,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { UserInfo user = mUserManager.getUserInfo(userHandle); final List profiles; - if (user.isManagedProfile() || LockPatternUtils.isSeparateWorkChallengeEnabled()) { - // If we are being asked about a managed profile or the main user profile has a - // separate lock from the work profile, just return keyguard features disabled - // by admins in the profile. + if (user.isManagedProfile()) { + // If we are being asked about a managed profile, just return keyguard features + // disabled by admins in the profile. profiles = Collections.singletonList(user); } else { // Otherwise return those set by admins in the user @@ -4669,9 +4676,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // If we are being asked explictly about this user // return all disabled features even if its a managed profile. which |= admin.disabledKeyguardFeatures; - } else { + } else if (!mLockPatternUtils.isSeparateProfileChallengeEnabled( + userInfo.id)) { // Otherwise a managed profile is only allowed to disable - // some features on the parent user. + // some features on the parent user, and we only aggregate them if + // it doesn't have its own challenge. which |= (admin.disabledKeyguardFeatures & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER); }