blob: b3cc8d0c31d71e09e7357053c60d1ab421da67c4 [file] [log] [blame]
Kelvin Kwanc8b00012020-01-31 00:58:19 +00001/*
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
17package com.android.documentsui;
18
19import static androidx.core.util.Preconditions.checkNotNull;
20
21import static com.android.documentsui.base.SharedMinimal.DEBUG;
22
23import android.Manifest;
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000024import android.content.BroadcastReceiver;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000025import android.content.Context;
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000026import android.content.Intent;
27import android.content.IntentFilter;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000028import android.content.pm.PackageManager;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000029import android.os.UserHandle;
30import android.os.UserManager;
31import android.util.Log;
32
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000033import androidx.annotation.GuardedBy;
Kelvin Kwana34bbd92020-02-10 23:49:21 +000034import androidx.annotation.Nullable;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000035import androidx.annotation.VisibleForTesting;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000036
Kelvin Kwan9feaaf52020-02-13 15:43:31 +000037import com.android.documentsui.base.Features;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000038import com.android.documentsui.base.UserId;
Kelvin Kwand4a4ac72020-02-26 21:59:33 +000039import com.android.documentsui.util.VersionUtils;
Kelvin Kwanc8b00012020-01-31 00:58:19 +000040
41import java.util.ArrayList;
42import java.util.List;
43
Kelvin Kwanc8b00012020-01-31 00:58:19 +000044/**
45 * Interface to query user ids.
46 */
47public interface UserIdManager {
48
49 /**
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000050 * Returns the {@UserId} of each profile which should be queried for documents. This will always
51 * include {@link UserId#CURRENT_USER}.
Kelvin Kwanc8b00012020-01-31 00:58:19 +000052 */
53 List<UserId> getUserIds();
54
55 /**
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000056 * Returns the system user from {@link #getUserIds()} if the list at least 2 users. Otherwise,
57 * returns null.
58 */
Kelvin Kwana34bbd92020-02-10 23:49:21 +000059 @Nullable
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000060 UserId getSystemUser();
61
62 /**
63 * Returns the managed user from {@link #getUserIds()} if the list at least 2 users. Otherwise,
64 * returns null.
65 */
Kelvin Kwana34bbd92020-02-10 23:49:21 +000066 @Nullable
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000067 UserId getManagedUser();
68
69 /**
Kelvin Kwanc8b00012020-01-31 00:58:19 +000070 * Creates an implementation of {@link UserIdManager}.
71 */
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
Kelvin Kwanc8b00012020-01-31 00:58:19 +000083 private final Context mContext;
84 private final UserId mCurrentUser;
85 private final boolean mIsDeviceSupported;
86
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +000087 @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
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000104 private RuntimeUserIdManager(Context context) {
105 this(context, UserId.CURRENT_USER,
Kelvin Kwan9feaaf52020-02-13 15:43:31 +0000106 Features.CROSS_PROFILE_TABS && isDeviceSupported(context));
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000107 }
108
109 @VisibleForTesting
110 RuntimeUserIdManager(Context context, UserId currentUser, boolean isDeviceSupported) {
111 mContext = context.getApplicationContext();
112 mCurrentUser = checkNotNull(currentUser);
113 mIsDeviceSupported = isDeviceSupported;
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +0000114
115
116 IntentFilter filter = new IntentFilter();
Kelvin Kwan34810bc2020-03-02 16:52:59 +0000117 filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
118 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +0000119 mContext.registerReceiver(mIntentReceiver, filter);
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000120 }
121
122 @Override
123 public List<UserId> getUserIds() {
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +0000124 synchronized (mUserIds) {
125 if (mUserIds.isEmpty()) {
126 mUserIds.addAll(getUserIdsInternal());
127 }
128 }
129 return mUserIds;
130 }
131
132 @Override
133 public UserId getSystemUser() {
134 synchronized (mUserIds) {
135 if (mUserIds.isEmpty()) {
136 getUserIds();
137 }
138 }
139 return mSystemUser;
140 }
141
142 @Override
143 public UserId getManagedUser() {
144 synchronized (mUserIds) {
145 if (mUserIds.isEmpty()) {
146 getUserIds();
147 }
148 }
149 return mManagedUser;
150 }
151
Kelvin Kwana34bbd92020-02-10 23:49:21 +0000152 private List<UserId> getUserIdsInternal() {
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000153 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;
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +0000174
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000175 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 }
Kelvin Kwan1b9f85b2020-02-13 15:22:38 +0000208 mSystemUser = systemUser;
209 mManagedUser = managedUser;
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000210 return result;
211 }
212
213 private static boolean isDeviceSupported(Context context) {
214 // The feature requires Android R DocumentsContract APIs and INTERACT_ACROSS_USERS
215 // permission.
Kelvin Kwand4a4ac72020-02-26 21:59:33 +0000216 return VersionUtils.isAtLeastR()
Kelvin Kwanc8b00012020-01-31 00:58:19 +0000217 && context.checkSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS)
218 == PackageManager.PERMISSION_GRANTED;
219 }
220 }
221}