• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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