1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.settings.users; 18 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.content.pm.UserInfo; 22 import android.os.RemoteException; 23 import android.os.UserHandle; 24 import android.os.UserManager; 25 import android.provider.Settings; 26 import android.util.Log; 27 28 import com.android.internal.widget.LockPatternUtils; 29 import com.android.internal.widget.VerifyCredentialResponse; 30 31 /** 32 * Manipulate and list restricted profiles on the device. 33 */ 34 public class RestrictedProfileModel { 35 private static final String TAG = "RestrictedProfile"; 36 37 private final Context mContext; 38 private final boolean mApplyRestrictions; 39 40 private final ActivityManager mActivityManager; 41 private final UserManager mUserManager; 42 43 /** Cache the UserInfo we're running as because, unlike other profiles, it won't change. */ 44 private final UserInfo mCurrentUserInfo; 45 RestrictedProfileModel(final Context context)46 public RestrictedProfileModel(final Context context) { 47 this(context, /* applyRestrictions= */ true); 48 } 49 RestrictedProfileModel(final Context context, final boolean applyRestrictions)50 RestrictedProfileModel(final Context context, final boolean applyRestrictions) { 51 mContext = context; 52 mApplyRestrictions = applyRestrictions; 53 54 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 55 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 56 mCurrentUserInfo = mUserManager.getUserInfo(mContext.getUserId()); 57 } 58 59 /** Switch into the restricted profile. */ enterUser()60 public boolean enterUser() { 61 if (isCurrentUser()) { 62 Log.w(TAG, "Tried to switch into current user"); 63 return false; 64 } 65 final UserInfo restrictedUser = getUser(); 66 if (restrictedUser == null) { 67 Log.e(TAG, "Tried to enter non-existent restricted user"); 68 return false; 69 } 70 updateBackgroundRestriction(restrictedUser); 71 switchUserNow(restrictedUser.id); 72 return true; 73 } 74 75 /** Switch out of the restricted profile, back into the primary user. */ exitUser()76 public void exitUser() { 77 if (isCurrentUser()) { 78 switchUserNow(getOwnerUserId()); 79 } 80 } 81 82 /** 83 * Remove the restricted profile. 84 * 85 * Called from another user. Requires permission to MANAGE_USERS. 86 */ removeUser()87 public void removeUser() { 88 final UserInfo restrictedUser = getUser(); 89 if (restrictedUser == null) { 90 Log.w(TAG, "No restricted user to remove?"); 91 return; 92 } 93 final int restrictedUserHandle = restrictedUser.id; 94 mUserManager.removeUser(restrictedUserHandle); 95 } 96 97 /** @return {@code true} if the current user is the restricted profile. */ isCurrentUser()98 public boolean isCurrentUser() { 99 return mCurrentUserInfo.isRestricted(); 100 } 101 102 /** 103 * @return a @{link UserInfo} for the restricted profile, or {@code null} if there is no 104 * restricted profile on the device. 105 */ getUser()106 public UserInfo getUser() { 107 if (mCurrentUserInfo.isRestricted()) { 108 return mCurrentUserInfo; 109 } 110 for (UserInfo userInfo : mUserManager.getUsers()) { 111 if (userInfo.isRestricted()) { 112 return userInfo; 113 } 114 } 115 return null; 116 } 117 118 /** 119 * @return user ID for the current user, or parent of the current user if it exists. 120 */ getOwnerUserId()121 private int getOwnerUserId() { 122 if (!mCurrentUserInfo.isRestricted()) { 123 return mCurrentUserInfo.id; 124 } else if (mCurrentUserInfo.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) { 125 return UserHandle.USER_OWNER; 126 } else { 127 return mCurrentUserInfo.restrictedProfileParentId; 128 } 129 } 130 131 /** Switch to {@param userId} or log an exception if this fails. */ switchUserNow(int userId)132 private void switchUserNow(int userId) { 133 try { 134 mActivityManager.switchUser(userId); 135 } catch (RuntimeException e) { 136 Log.e(TAG, "Caught exception while switching user! ", e); 137 } 138 } 139 140 /** 141 * Profiles are allowed to run in the background by default, unless the device specifically 142 * sets a config flag and/or has the global setting overridden by something on-device. 143 */ updateBackgroundRestriction(UserInfo user)144 private void updateBackgroundRestriction(UserInfo user) { 145 if (!mApplyRestrictions) { 146 return; 147 } 148 final boolean allowedToRun = shouldAllowRunInBackground(); 149 mUserManager.setUserRestriction( 150 UserManager.DISALLOW_RUN_IN_BACKGROUND, !allowedToRun, user.getUserHandle()); 151 } 152 153 /** 154 * @see #updateBackgroundRestriction(UserInfo) 155 * @see Settings.Global#KEEP_PROFILE_IN_BACKGROUND 156 */ shouldAllowRunInBackground()157 private boolean shouldAllowRunInBackground() { 158 final boolean defaultValue = mContext.getResources().getBoolean( 159 com.android.internal.R.bool.config_keepRestrictedProfilesInBackground); 160 return Settings.Global.getInt(mContext.getContentResolver(), 161 Settings.Global.KEEP_PROFILE_IN_BACKGROUND, defaultValue ? 1 : 0) > 0; 162 } 163 164 /** 165 * @return {@code true} if {@code password} is a correct PIN for exiting the restricted user. 166 */ checkPassword(String password)167 public boolean checkPassword(String password) { 168 try { 169 final int userId = getOwnerUserId(); 170 final byte[] passwordBytes = password != null ? password.getBytes() : null; 171 return VerifyCredentialResponse.RESPONSE_OK 172 == new LockPatternUtils(mContext).getLockSettings().checkCredential( 173 passwordBytes, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, userId, null) 174 .getResponseCode(); 175 } catch (RemoteException e) { 176 Log.e(TAG, "Unable to check password for unlocking the user", e); 177 } 178 return false; 179 } 180 181 /** 182 * @return {@code true} if the owner user has a PIN set to prevent access from the restricted 183 * profile. 184 */ hasLockscreenSecurity()185 public boolean hasLockscreenSecurity() { 186 final int userId = getOwnerUserId(); 187 final LockPatternUtils lpu = new LockPatternUtils(mContext); 188 return lpu.isLockPasswordEnabled(userId) || lpu.isLockPatternEnabled(userId); 189 } 190 } 191