1 /*
2  * Copyright (C) 2021 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.UserHandle;
23 import android.os.UserManager;
24 import android.provider.Settings;
25 import android.util.Log;
26 
27 /**
28  * Manipulate and list restricted profiles on the device.
29  */
30 public class RestrictedProfileModel {
31     private static final String TAG = "RestrictedProfile";
32 
33     private final Context mContext;
34     private final boolean mApplyRestrictions;
35 
36     private final ActivityManager mActivityManager;
37     private final UserManager mUserManager;
38 
39     /** Cache the UserInfo we're running as because, unlike other profiles, it won't change. */
40     private final UserInfo mCurrentUserInfo;
41 
RestrictedProfileModel(final Context context)42     public RestrictedProfileModel(final Context context) {
43         this(context, /* applyRestrictions= */ true);
44     }
45 
RestrictedProfileModel(final Context context, final boolean applyRestrictions)46     public RestrictedProfileModel(final Context context, final boolean applyRestrictions) {
47         mContext = context;
48         mApplyRestrictions = applyRestrictions;
49 
50         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
51         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
52         mCurrentUserInfo = mUserManager.getUserInfo(mContext.getUserId());
53     }
54 
55     /** Switch into the restricted profile. */
enterUser()56     public boolean enterUser() {
57         if (isCurrentUser()) {
58             Log.w(TAG, "Tried to switch into current user");
59             return false;
60         }
61         final UserInfo restrictedUser = getUser();
62         if (restrictedUser == null) {
63             Log.e(TAG, "Tried to enter non-existent restricted user");
64             return false;
65         }
66         updateBackgroundRestriction(restrictedUser);
67         switchUserNow(restrictedUser.id);
68         return true;
69     }
70 
71     /** Switch out of the restricted profile, back into the primary user. */
exitUser()72     public void exitUser() {
73         if (isCurrentUser()) {
74             switchUserNow(getOwnerUserId());
75         }
76     }
77 
78     /**
79      * Remove the restricted profile.
80      *
81      * Called from another user. Requires permission to MANAGE_USERS.
82      */
removeUser()83     public void removeUser() {
84         final UserInfo restrictedUser = getUser();
85         if (restrictedUser == null) {
86             Log.w(TAG, "No restricted user to remove?");
87             return;
88         }
89         final int restrictedUserHandle = restrictedUser.id;
90         mUserManager.removeUser(restrictedUserHandle);
91     }
92 
93     /** @return {@code true} if the current user is the restricted profile. */
isCurrentUser()94     public boolean isCurrentUser() {
95         return mCurrentUserInfo.isRestricted();
96     }
97 
98     /**
99      * @return a @{link UserInfo} for the restricted profile, or {@code null} if there is no
100      * restricted profile on the device.
101      */
getUser()102     public UserInfo getUser() {
103         if (mCurrentUserInfo.isRestricted()) {
104             return mCurrentUserInfo;
105         }
106         for (UserInfo userInfo : mUserManager.getUsers()) {
107             if (userInfo.isRestricted()) {
108                 return userInfo;
109             }
110         }
111         return null;
112     }
113 
114     /**
115      * @return user ID for the current user, or parent of the current user if it exists.
116      */
getOwnerUserId()117     private int getOwnerUserId() {
118         if (!mCurrentUserInfo.isRestricted()) {
119             return mCurrentUserInfo.id;
120         } else if (mCurrentUserInfo.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
121             return UserHandle.USER_OWNER;
122         } else {
123             return mCurrentUserInfo.restrictedProfileParentId;
124         }
125     }
126 
127     /** Switch to {@param userId} or log an exception if this fails. */
switchUserNow(int userId)128     private void switchUserNow(int userId) {
129         try {
130             mActivityManager.switchUser(userId);
131         } catch (RuntimeException e) {
132             Log.e(TAG, "Caught exception while switching user! ", e);
133         }
134     }
135 
136     /**
137      * Profiles are allowed to run in the background by default, unless the device specifically
138      * sets a config flag and/or has the global setting overridden by something on-device.
139      */
updateBackgroundRestriction(UserInfo user)140     private void updateBackgroundRestriction(UserInfo user) {
141         if (!mApplyRestrictions) {
142             return;
143         }
144         final boolean allowedToRun = shouldAllowRunInBackground();
145         mUserManager.setUserRestriction(
146                 UserManager.DISALLOW_RUN_IN_BACKGROUND, !allowedToRun, user.getUserHandle());
147     }
148 
149     /**
150      * @see #updateBackgroundRestriction(UserInfo)
151      * @see Settings.Global#KEEP_PROFILE_IN_BACKGROUND
152      */
shouldAllowRunInBackground()153     private boolean shouldAllowRunInBackground() {
154         final boolean defaultValue = mContext.getResources().getBoolean(
155                 mContext.getResources().getIdentifier("config_keepRestrictedProfilesInBackground",
156                         "bool", "android"));
157         return Settings.Global.getInt(mContext.getContentResolver(),
158                 Settings.Global.KEEP_PROFILE_IN_BACKGROUND, defaultValue ? 1 : 0) > 0;
159     }
160 }
161