1 /* 2 * Copyright (C) 2020 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.documentsui; 18 19 import static androidx.core.util.Preconditions.checkNotNull; 20 21 import static com.android.documentsui.base.SharedMinimal.DEBUG; 22 23 import android.Manifest; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.PackageManager; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.util.Log; 32 33 import androidx.annotation.GuardedBy; 34 import androidx.annotation.Nullable; 35 import androidx.annotation.VisibleForTesting; 36 37 import com.android.documentsui.base.Features; 38 import com.android.documentsui.base.UserId; 39 import com.android.documentsui.util.VersionUtils; 40 41 import java.util.ArrayList; 42 import java.util.List; 43 44 /** 45 * Interface to query user ids. 46 */ 47 public interface UserIdManager { 48 49 /** 50 * Returns the {@UserId} of each profile which should be queried for documents. This will always 51 * include {@link UserId#CURRENT_USER}. 52 */ getUserIds()53 List<UserId> getUserIds(); 54 55 /** 56 * Returns the system user from {@link #getUserIds()} if the list at least 2 users. Otherwise, 57 * returns null. 58 */ 59 @Nullable getSystemUser()60 UserId getSystemUser(); 61 62 /** 63 * Returns the managed user from {@link #getUserIds()} if the list at least 2 users. Otherwise, 64 * returns null. 65 */ 66 @Nullable getManagedUser()67 UserId getManagedUser(); 68 69 /** 70 * Creates an implementation of {@link UserIdManager}. 71 */ create(Context context)72 static UserIdManager create(Context context) { 73 return new RuntimeUserIdManager(context); 74 } 75 76 /** 77 * Implementation of {@link UserIdManager}. 78 */ 79 final class RuntimeUserIdManager implements UserIdManager { 80 81 private static final String TAG = "UserIdManager"; 82 83 private final Context mContext; 84 private final UserId mCurrentUser; 85 private final boolean mIsDeviceSupported; 86 87 @GuardedBy("mUserIds") 88 private final List<UserId> mUserIds = new ArrayList<>(); 89 @GuardedBy("mUserIds") 90 private UserId mSystemUser = null; 91 @GuardedBy("mUserIds") 92 private UserId mManagedUser = null; 93 94 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 95 96 @Override 97 public void onReceive(Context context, Intent intent) { 98 synchronized (mUserIds) { 99 mUserIds.clear(); 100 } 101 } 102 }; 103 RuntimeUserIdManager(Context context)104 private RuntimeUserIdManager(Context context) { 105 this(context, UserId.CURRENT_USER, 106 Features.CROSS_PROFILE_TABS && isDeviceSupported(context)); 107 } 108 109 @VisibleForTesting RuntimeUserIdManager(Context context, UserId currentUser, boolean isDeviceSupported)110 RuntimeUserIdManager(Context context, UserId currentUser, boolean isDeviceSupported) { 111 mContext = context.getApplicationContext(); 112 mCurrentUser = checkNotNull(currentUser); 113 mIsDeviceSupported = isDeviceSupported; 114 115 116 IntentFilter filter = new IntentFilter(); 117 filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); 118 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 119 mContext.registerReceiver(mIntentReceiver, filter); 120 } 121 122 @Override getUserIds()123 public List<UserId> getUserIds() { 124 synchronized (mUserIds) { 125 if (mUserIds.isEmpty()) { 126 mUserIds.addAll(getUserIdsInternal()); 127 } 128 } 129 return mUserIds; 130 } 131 132 @Override getSystemUser()133 public UserId getSystemUser() { 134 synchronized (mUserIds) { 135 if (mUserIds.isEmpty()) { 136 getUserIds(); 137 } 138 } 139 return mSystemUser; 140 } 141 142 @Override getManagedUser()143 public UserId getManagedUser() { 144 synchronized (mUserIds) { 145 if (mUserIds.isEmpty()) { 146 getUserIds(); 147 } 148 } 149 return mManagedUser; 150 } 151 getUserIdsInternal()152 private List<UserId> getUserIdsInternal() { 153 final List<UserId> result = new ArrayList<>(); 154 result.add(mCurrentUser); 155 156 // If the feature is disabled, return a list just containing the current user. 157 if (!mIsDeviceSupported) { 158 return result; 159 } 160 161 UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 162 if (userManager == null) { 163 Log.e(TAG, "cannot obtain user manager"); 164 return result; 165 } 166 167 final List<UserHandle> userProfiles = userManager.getUserProfiles(); 168 if (userProfiles.size() < 2) { 169 return result; 170 } 171 172 UserId systemUser = null; 173 UserId managedUser = null; 174 175 for (UserHandle userHandle : userProfiles) { 176 if (userHandle.isSystem()) { 177 systemUser = UserId.of(userHandle); 178 continue; 179 } 180 if (managedUser == null 181 && userManager.isManagedProfile(userHandle.getIdentifier())) { 182 managedUser = UserId.of(userHandle); 183 } 184 } 185 186 if (mCurrentUser.isSystem()) { 187 // 1. If the current user is system (personal), add the managed user. 188 if (managedUser != null) { 189 result.add(managedUser); 190 } 191 } else if (mCurrentUser.isManagedProfile(userManager)) { 192 // 2. If the current user is a managed user, add the personal user. 193 // Since we don't have MANAGED_USERS permission to get the parent user, we will 194 // treat the system as personal although the system can theoretically in the profile 195 // group but not being the parent user(personal) of the managed user. 196 if (systemUser != null) { 197 result.add(0, systemUser); 198 } 199 } else { 200 // 3. If we cannot resolve the users properly, we will disable the cross-profile 201 // feature by returning just the current user. 202 if (DEBUG) { 203 Log.w(TAG, "The current user " + UserId.CURRENT_USER 204 + " is neither system nor managed user. has system user: " 205 + (systemUser != null)); 206 } 207 } 208 mSystemUser = systemUser; 209 mManagedUser = managedUser; 210 return result; 211 } 212 isDeviceSupported(Context context)213 private static boolean isDeviceSupported(Context context) { 214 // The feature requires Android R DocumentsContract APIs and INTERACT_ACROSS_USERS 215 // permission. 216 return VersionUtils.isAtLeastR() 217 && context.checkSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS) 218 == PackageManager.PERMISSION_GRANTED; 219 } 220 } 221 } 222