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