• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
22 import static com.android.documentsui.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
23 import static com.android.documentsui.DevicePolicyResources.Strings.PERSONAL_TAB;
24 import static com.android.documentsui.DevicePolicyResources.Strings.WORK_TAB;
25 import static com.android.documentsui.base.SharedMinimal.DEBUG;
26 
27 import android.Manifest;
28 import android.annotation.SuppressLint;
29 import android.app.ActivityManager;
30 import android.app.admin.DevicePolicyManager;
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.pm.PackageManager;
36 import android.content.pm.UserProperties;
37 import android.graphics.drawable.Drawable;
38 import android.os.Build;
39 import android.os.UserHandle;
40 import android.os.UserManager;
41 import android.util.Log;
42 
43 import androidx.annotation.GuardedBy;
44 import androidx.annotation.RequiresApi;
45 import androidx.annotation.VisibleForTesting;
46 
47 import com.android.documentsui.base.Features;
48 import com.android.documentsui.base.UserId;
49 import com.android.documentsui.util.CrossProfileUtils;
50 import com.android.documentsui.util.VersionUtils;
51 import com.android.modules.utils.build.SdkLevel;
52 
53 import com.google.common.base.Objects;
54 
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59 
60 @RequiresApi(Build.VERSION_CODES.S)
61 public interface UserManagerState {
62 
63     /**
64      * Returns the {@link UserId} of each profile which should be queried for documents. This will
65      * always
66      * include {@link UserId#CURRENT_USER}.
67      */
getUserIds()68     List<UserId> getUserIds();
69 
70     /**
71      * Returns mapping between the {@link UserId} and the label for the profile
72      */
getUserIdToLabelMap()73     Map<UserId, String> getUserIdToLabelMap();
74 
75     /**
76      * Returns mapping between the {@link UserId} and the drawable badge for the profile
77      *
78      * returns {@code null} for non-profile userId
79      */
getUserIdToBadgeMap()80     Map<UserId, Drawable> getUserIdToBadgeMap();
81 
82     /**
83      * Returns a map of {@link UserId} to boolean value indicating whether
84      * the {@link UserId}.CURRENT_USER can forward {@link Intent} to that {@link UserId}
85      */
getCanForwardToProfileIdMap(Intent intent)86     Map<UserId, Boolean> getCanForwardToProfileIdMap(Intent intent);
87 
88     /**
89      * Updates the state of the list of userIds and all the associated maps according the intent
90      * received in broadcast
91      *
92      * @param userId {@link UserId} for the profile for which the availability status changed
93      * @param action {@link Intent}.ACTION_PROFILE_UNAVAILABLE and {@link
94      *     Intent}.ACTION_PROFILE_AVAILABLE, {@link Intent}.ACTION_PROFILE_ADDED} and {@link
95      *     Intent}.ACTION_PROFILE_REMOVED}
96      */
onProfileActionStatusChange(String action, UserId userId)97     void onProfileActionStatusChange(String action, UserId userId);
98 
99     /**
100      * Sets the intent that triggered the launch of the DocsUI
101      */
setCurrentStateIntent(Intent intent)102     void setCurrentStateIntent(Intent intent);
103 
104     /** Returns true if there are hidden profiles */
areHiddenInQuietModeProfilesPresent()105     boolean areHiddenInQuietModeProfilesPresent();
106 
107     /**
108      * Creates an implementation of {@link UserManagerState}.
109      */
110     // TODO: b/314746383 Make this class a singleton
create(Context context)111     static UserManagerState create(Context context) {
112         return new RuntimeUserManagerState(context);
113     }
114 
115     /**
116      * Implementation of {@link UserManagerState}
117      */
118     final class RuntimeUserManagerState implements UserManagerState {
119 
120         private static final String TAG = "UserManagerState";
121         private final Context mContext;
122         private final UserId mCurrentUser;
123         private final boolean mIsDeviceSupported;
124         private final UserManager mUserManager;
125         private final ConfigStore mConfigStore;
126         /**
127          * List of all the {@link UserId} that have the {@link UserProperties.ShowInSharingSurfaces}
128          * set as `SHOW_IN_SHARING_SURFACES_SEPARATE` OR it is a system/personal user
129          */
130         @GuardedBy("mUserIds")
131         private final List<UserId> mUserIds = new ArrayList<>();
132         /**
133          * Mapping between the {@link UserId} to the corresponding profile label
134          */
135         @GuardedBy("mUserIdToLabelMap")
136         private final Map<UserId, String> mUserIdToLabelMap = new HashMap<>();
137         /**
138          * Mapping between the {@link UserId} to the corresponding profile badge
139          */
140         @GuardedBy("mUserIdToBadgeMap")
141         private final Map<UserId, Drawable> mUserIdToBadgeMap = new HashMap<>();
142         /**
143          * Map containing {@link UserId}, other than that of the current user, as key and boolean
144          * denoting whether it is accessible by the current user or not as value
145          */
146         @GuardedBy("mCanFrowardToProfileIdMap")
147         private final Map<UserId, Boolean> mCanFrowardToProfileIdMap = new HashMap<>();
148 
149         private Intent mCurrentStateIntent;
150 
151         private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
152             @Override
153             public void onReceive(Context context, Intent intent) {
154                 synchronized (mUserIds) {
155                     mUserIds.clear();
156                 }
157                 synchronized (mUserIdToLabelMap) {
158                     mUserIdToLabelMap.clear();
159                 }
160                 synchronized (mUserIdToBadgeMap) {
161                     mUserIdToBadgeMap.clear();
162                 }
163                 synchronized (mCanFrowardToProfileIdMap) {
164                     mCanFrowardToProfileIdMap.clear();
165                 }
166             }
167         };
168 
169 
RuntimeUserManagerState(Context context)170         private RuntimeUserManagerState(Context context) {
171             this(context, UserId.CURRENT_USER,
172                     Features.CROSS_PROFILE_TABS && isDeviceSupported(context),
173                     DocumentsApplication.getConfigStore());
174         }
175 
176         @VisibleForTesting
RuntimeUserManagerState(Context context, UserId currentUser, boolean isDeviceSupported, ConfigStore configStore)177         RuntimeUserManagerState(Context context, UserId currentUser, boolean isDeviceSupported,
178                 ConfigStore configStore) {
179             mContext = context.getApplicationContext();
180             mCurrentUser = checkNotNull(currentUser);
181             mIsDeviceSupported = isDeviceSupported;
182             mUserManager = mContext.getSystemService(UserManager.class);
183             mConfigStore = configStore;
184 
185             IntentFilter filter = new IntentFilter();
186             filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
187             filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
188             if (SdkLevel.isAtLeastV() && mConfigStore.isPrivateSpaceInDocsUIEnabled()) {
189                 filter.addAction(Intent.ACTION_PROFILE_ADDED);
190                 filter.addAction(Intent.ACTION_PROFILE_REMOVED);
191             }
192             mContext.registerReceiver(mIntentReceiver, filter);
193         }
194 
195         @Override
getUserIds()196         public List<UserId> getUserIds() {
197             synchronized (mUserIds) {
198                 if (mUserIds.isEmpty()) {
199                     mUserIds.addAll(getUserIdsInternal());
200                 }
201                 return mUserIds;
202             }
203         }
204 
205         @Override
getUserIdToLabelMap()206         public Map<UserId, String> getUserIdToLabelMap() {
207             synchronized (mUserIdToLabelMap) {
208                 if (mUserIdToLabelMap.isEmpty()) {
209                     getUserIdToLabelMapInternal();
210                 }
211                 return mUserIdToLabelMap;
212             }
213         }
214 
215         @Override
getUserIdToBadgeMap()216         public Map<UserId, Drawable> getUserIdToBadgeMap() {
217             synchronized (mUserIdToBadgeMap) {
218                 if (mUserIdToBadgeMap.isEmpty()) {
219                     getUserIdToBadgeMapInternal();
220                 }
221                 return mUserIdToBadgeMap;
222             }
223         }
224 
225         @Override
getCanForwardToProfileIdMap(Intent intent)226         public Map<UserId, Boolean> getCanForwardToProfileIdMap(Intent intent) {
227             synchronized (mCanFrowardToProfileIdMap) {
228                 if (mCanFrowardToProfileIdMap.isEmpty()) {
229                     getCanForwardToProfileIdMapInternal(intent);
230                 }
231                 return mCanFrowardToProfileIdMap;
232             }
233         }
234 
235         @Override
236         @SuppressLint("NewApi")
onProfileActionStatusChange(String action, UserId userId)237         public void onProfileActionStatusChange(String action, UserId userId) {
238             if (!SdkLevel.isAtLeastV()) return;
239             UserProperties userProperties = mUserManager.getUserProperties(
240                     UserHandle.of(userId.getIdentifier()));
241             if (userProperties.getShowInQuietMode() != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
242                 return;
243             }
244             if (Intent.ACTION_PROFILE_UNAVAILABLE.equals(action)
245                     || Intent.ACTION_PROFILE_REMOVED.equals(action)) {
246                 synchronized (mUserIds) {
247                     mUserIds.remove(userId);
248                 }
249             } else if (Intent.ACTION_PROFILE_AVAILABLE.equals(action)
250                     || Intent.ACTION_PROFILE_ADDED.equals(action)) {
251                 synchronized (mUserIds) {
252                     if (!mUserIds.contains(userId)) {
253                         mUserIds.add(userId);
254                     }
255                 }
256                 synchronized (mUserIdToLabelMap) {
257                     if (!mUserIdToLabelMap.containsKey(userId)) {
258                         mUserIdToLabelMap.put(userId, getProfileLabel(userId));
259                     }
260                 }
261                 synchronized (mUserIdToBadgeMap) {
262                     if (!mUserIdToBadgeMap.containsKey(userId)) {
263                         mUserIdToBadgeMap.put(userId, getProfileBadge(userId));
264                     }
265                 }
266                 synchronized (mCanFrowardToProfileIdMap) {
267                     if (!mCanFrowardToProfileIdMap.containsKey(userId)) {
268                         if (userId.getIdentifier() == ActivityManager.getCurrentUser()
269                                 || isCrossProfileContentSharingStrategyDelegatedFromParent(
270                                 UserHandle.of(userId.getIdentifier()))
271                                 || CrossProfileUtils.getCrossProfileResolveInfo(mCurrentUser,
272                                 mContext.getPackageManager(), mCurrentStateIntent, mContext,
273                                 mConfigStore.isPrivateSpaceInDocsUIEnabled()) != null) {
274                             mCanFrowardToProfileIdMap.put(userId, true);
275                         } else {
276                             mCanFrowardToProfileIdMap.put(userId, false);
277                         }
278 
279                     }
280                 }
281             } else {
282                 Log.e(TAG, "Unexpected action received: " + action);
283             }
284         }
285 
286         @Override
setCurrentStateIntent(Intent intent)287         public void setCurrentStateIntent(Intent intent) {
288             mCurrentStateIntent = intent;
289         }
290 
291         @Override
areHiddenInQuietModeProfilesPresent()292         public boolean areHiddenInQuietModeProfilesPresent() {
293             if (!SdkLevel.isAtLeastV()) {
294                 return false;
295             }
296 
297             for (UserId userId : getUserIds()) {
298                 if (mUserManager
299                                 .getUserProperties(UserHandle.of(userId.getIdentifier()))
300                                 .getShowInQuietMode()
301                         == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
302                     return true;
303                 }
304             }
305             return false;
306         }
307 
getUserIdsInternal()308         private List<UserId> getUserIdsInternal() {
309             final List<UserId> result = new ArrayList<>();
310 
311             if (!mIsDeviceSupported) {
312                 result.add(mCurrentUser);
313                 return result;
314             }
315 
316             if (mUserManager == null) {
317                 Log.e(TAG, "cannot obtain user manager");
318                 result.add(mCurrentUser);
319                 return result;
320             }
321 
322             final List<UserHandle> userProfiles = mUserManager.getUserProfiles();
323             if (userProfiles.size() < 2) {
324                 result.add(mCurrentUser);
325                 return result;
326             }
327 
328             if (SdkLevel.isAtLeastV()) {
329                 getUserIdsInternalPostV(userProfiles, result);
330             } else {
331                 getUserIdsInternalPreV(userProfiles, result);
332             }
333             return result;
334         }
335 
336         @SuppressLint("NewApi")
getUserIdsInternalPostV(List<UserHandle> userProfiles, List<UserId> result)337         private void getUserIdsInternalPostV(List<UserHandle> userProfiles, List<UserId> result) {
338             for (UserHandle userHandle : userProfiles) {
339                 if (userHandle.getIdentifier() == ActivityManager.getCurrentUser()) {
340                     result.add(UserId.of(userHandle));
341                 } else {
342                     // Out of all the profiles returned by user manager the profiles that are
343                     // returned should satisfy both the following conditions:
344                     // 1. It has user property SHOW_IN_SHARING_SURFACES_SEPARATE
345                     // 2. Quite mode is not enabled, if it is enabled then the profile's user
346                     //    property is not SHOW_IN_QUIET_MODE_HIDDEN
347                     if (isProfileAllowed(userHandle)) {
348                         result.add(UserId.of(userHandle));
349                     }
350                 }
351             }
352             if (result.isEmpty()) {
353                 result.add(mCurrentUser);
354             }
355         }
356 
357         @SuppressLint("NewApi")
isProfileAllowed(UserHandle userHandle)358         private boolean isProfileAllowed(UserHandle userHandle) {
359             final UserProperties userProperties =
360                     mUserManager.getUserProperties(userHandle);
361             if (userProperties.getShowInSharingSurfaces()
362                     == UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) {
363                 return !UserId.of(userHandle).isQuietModeEnabled(mContext)
364                         || userProperties.getShowInQuietMode()
365                         != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
366             }
367             return false;
368         }
369 
getUserIdsInternalPreV(List<UserHandle> userProfiles, List<UserId> result)370         private void getUserIdsInternalPreV(List<UserHandle> userProfiles, List<UserId> result) {
371             result.add(mCurrentUser);
372             UserId systemUser = null;
373             UserId managedUser = null;
374             for (UserHandle userHandle : userProfiles) {
375                 if (userHandle.isSystem()) {
376                     systemUser = UserId.of(userHandle);
377                 } else if (mUserManager.isManagedProfile(userHandle.getIdentifier())) {
378                     managedUser = UserId.of(userHandle);
379                 }
380             }
381             if (mCurrentUser.isSystem() && managedUser != null) {
382                 result.add(managedUser);
383             } else if (mCurrentUser.isManagedProfile(mUserManager) && systemUser != null) {
384                 result.add(0, systemUser);
385             } else {
386                 if (DEBUG) {
387                     Log.w(TAG, "The current user " + UserId.CURRENT_USER
388                             + " is neither system nor managed user. has system user: "
389                             + (systemUser != null));
390                 }
391             }
392         }
393 
getUserIdToLabelMapInternal()394         private void getUserIdToLabelMapInternal() {
395             if (SdkLevel.isAtLeastV()) {
396                 getUserIdToLabelMapInternalPostV();
397             } else {
398                 getUserIdToLabelMapInternalPreV();
399             }
400         }
401 
402         @SuppressLint("NewApi")
getUserIdToLabelMapInternalPostV()403         private void getUserIdToLabelMapInternalPostV() {
404             if (mUserManager == null) {
405                 Log.e(TAG, "cannot obtain user manager");
406                 return;
407             }
408             List<UserId> userIds = getUserIds();
409             for (UserId userId : userIds) {
410                 synchronized (mUserIdToLabelMap) {
411                     mUserIdToLabelMap.put(userId, getProfileLabel(userId));
412                 }
413             }
414         }
415 
getUserIdToLabelMapInternalPreV()416         private void getUserIdToLabelMapInternalPreV() {
417             if (mUserManager == null) {
418                 Log.e(TAG, "cannot obtain user manager");
419                 return;
420             }
421             List<UserId> userIds = getUserIds();
422             for (UserId userId : userIds) {
423                 if (mUserManager.isManagedProfile(userId.getIdentifier())) {
424                     synchronized (mUserIdToLabelMap) {
425                         mUserIdToLabelMap.put(userId,
426                                 getEnterpriseString(WORK_TAB, R.string.work_tab));
427                     }
428                 } else {
429                     synchronized (mUserIdToLabelMap) {
430                         mUserIdToLabelMap.put(userId,
431                                 getEnterpriseString(PERSONAL_TAB, R.string.personal_tab));
432                     }
433                 }
434             }
435         }
436 
437         @SuppressLint("NewApi")
getProfileLabel(UserId userId)438         private String getProfileLabel(UserId userId) {
439             if (userId.getIdentifier() == ActivityManager.getCurrentUser()) {
440                 return getEnterpriseString(PERSONAL_TAB, R.string.personal_tab);
441             }
442             try {
443                 Context userContext = mContext.createContextAsUser(
444                         UserHandle.of(userId.getIdentifier()), 0 /* flags */);
445                 UserManager userManagerAsUser = userContext.getSystemService(UserManager.class);
446                 if (userManagerAsUser == null) {
447                     Log.e(TAG, "cannot obtain user manager");
448                     return null;
449                 }
450                 return userManagerAsUser.getProfileLabel();
451             } catch (Exception e) {
452                 Log.e(TAG, "Exception occurred while trying to get profile label:\n" + e);
453                 return null;
454             }
455         }
456 
getEnterpriseString(String updatableStringId, int defaultStringId)457         private String getEnterpriseString(String updatableStringId, int defaultStringId) {
458             if (SdkLevel.isAtLeastT()) {
459                 return getUpdatableEnterpriseString(updatableStringId, defaultStringId);
460             } else {
461                 return mContext.getString(defaultStringId);
462             }
463         }
464 
465         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
getUpdatableEnterpriseString(String updatableStringId, int defaultStringId)466         private String getUpdatableEnterpriseString(String updatableStringId, int defaultStringId) {
467             DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
468             if (Objects.equal(dpm, null)) {
469                 Log.e(TAG, "can not get device policy manager");
470                 return mContext.getString(defaultStringId);
471             }
472             return dpm.getResources().getString(
473                     updatableStringId,
474                     () -> mContext.getString(defaultStringId));
475         }
476 
getUserIdToBadgeMapInternal()477         private void getUserIdToBadgeMapInternal() {
478             if (SdkLevel.isAtLeastV()) {
479                 getUserIdToBadgeMapInternalPostV();
480             } else {
481                 getUserIdToBadgeMapInternalPreV();
482             }
483         }
484 
485         @SuppressLint("NewApi")
getUserIdToBadgeMapInternalPostV()486         private void getUserIdToBadgeMapInternalPostV() {
487             if (mUserManager == null) {
488                 Log.e(TAG, "cannot obtain user manager");
489                 return;
490             }
491             List<UserId> userIds = getUserIds();
492             for (UserId userId : userIds) {
493                 synchronized (mUserIdToBadgeMap) {
494                     mUserIdToBadgeMap.put(userId, getProfileBadge(userId));
495                 }
496             }
497         }
498 
getUserIdToBadgeMapInternalPreV()499         private void getUserIdToBadgeMapInternalPreV() {
500             if (!SdkLevel.isAtLeastR()) return;
501             if (mUserManager == null) {
502                 Log.e(TAG, "cannot obtain user manager");
503                 return;
504             }
505             List<UserId> userIds = getUserIds();
506             for (UserId userId : userIds) {
507                 if (mUserManager.isManagedProfile(userId.getIdentifier())) {
508                     synchronized (mUserIdToBadgeMap) {
509                         mUserIdToBadgeMap.put(userId,
510                                 SdkLevel.isAtLeastT() ? getWorkProfileBadge()
511                                         : mContext.getDrawable(R.drawable.ic_briefcase));
512                     }
513                 }
514             }
515         }
516 
517         @SuppressLint("NewApi")
getProfileBadge(UserId userId)518         private Drawable getProfileBadge(UserId userId) {
519             if (userId.getIdentifier() == ActivityManager.getCurrentUser()) {
520                 return null;
521             }
522             try {
523                 Context userContext = mContext.createContextAsUser(
524                         UserHandle.of(userId.getIdentifier()), 0 /* flags */);
525                 UserManager userManagerAsUser = userContext.getSystemService(UserManager.class);
526                 if (userManagerAsUser == null) {
527                     Log.e(TAG, "cannot obtain user manager");
528                     return null;
529                 }
530                 return userManagerAsUser.getUserBadge();
531             } catch (Exception e) {
532                 Log.e(TAG, "Exception occurred while trying to get profile badge:\n" + e);
533                 return null;
534             }
535         }
536 
537         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
getWorkProfileBadge()538         private Drawable getWorkProfileBadge() {
539             DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
540             Drawable drawable = dpm.getResources().getDrawable(WORK_PROFILE_ICON, SOLID_COLORED,
541                     () ->
542                             mContext.getDrawable(R.drawable.ic_briefcase));
543             return drawable;
544         }
545 
getCanForwardToProfileIdMapInternal(Intent intent)546         private void getCanForwardToProfileIdMapInternal(Intent intent) {
547             // Versions less than V will not have the user properties required to determine whether
548             // cross profile check is delegated from parent or not
549             if (!SdkLevel.isAtLeastV()) {
550                 getCanForwardToProfileIdMapPreV(intent);
551                 return;
552             }
553             if (mUserManager == null) {
554                 Log.e(TAG, "can not get user manager");
555                 return;
556             }
557 
558             List<UserId> parentOrDelegatedFromParent = new ArrayList<>();
559             List<UserId> canForwardToProfileIds = new ArrayList<>();
560             List<UserId> noDelegation = new ArrayList<>();
561 
562             List<UserId> userIds = getUserIds();
563             for (UserId userId : userIds) {
564                 final UserHandle userHandle = UserHandle.of(userId.getIdentifier());
565                 // Parent (personal) profile and all the child profiles that delegate cross profile
566                 // content sharing check to parent can share among each other
567                 if (userId.getIdentifier() == ActivityManager.getCurrentUser()
568                         || isCrossProfileContentSharingStrategyDelegatedFromParent(userHandle)) {
569                     parentOrDelegatedFromParent.add(userId);
570                 } else {
571                     noDelegation.add(userId);
572                 }
573             }
574 
575             if (noDelegation.size() > 1) {
576                 Log.e(TAG, "There cannot be more than one profile delegating cross profile "
577                         + "content sharing check from self.");
578             }
579 
580             /*
581              * Cross profile resolve info need to be checked in the following 2 cases:
582              * 1. current user is either parent or delegates check to parent and the target user
583              *    does not delegate to parent
584              * 2. current user does not delegate check to the parent and the target user is the
585              *    parent profile
586              */
587             UserId needToCheck = null;
588             if (parentOrDelegatedFromParent.contains(mCurrentUser)
589                     && !noDelegation.isEmpty()) {
590                 needToCheck = noDelegation.get(0);
591             } else if (mCurrentUser.getIdentifier() != ActivityManager.getCurrentUser()) {
592                 final UserHandle parentProfile = mUserManager.getProfileParent(
593                         UserHandle.of(mCurrentUser.getIdentifier()));
594                 needToCheck = UserId.of(parentProfile);
595             }
596 
597             if (needToCheck != null && CrossProfileUtils.getCrossProfileResolveInfo(mCurrentUser,
598                     mContext.getPackageManager(), intent, mContext,
599                     mConfigStore.isPrivateSpaceInDocsUIEnabled()) != null) {
600                 if (parentOrDelegatedFromParent.contains(needToCheck)) {
601                     canForwardToProfileIds.addAll(parentOrDelegatedFromParent);
602                 } else {
603                     canForwardToProfileIds.add(needToCheck);
604                 }
605             }
606 
607             if (parentOrDelegatedFromParent.contains(mCurrentUser)) {
608                 canForwardToProfileIds.addAll(parentOrDelegatedFromParent);
609             }
610 
611             for (UserId userId : userIds) {
612                 synchronized (mCanFrowardToProfileIdMap) {
613                     if (userId.equals(mCurrentUser)) {
614                         mCanFrowardToProfileIdMap.put(userId, true);
615                         continue;
616                     }
617                     mCanFrowardToProfileIdMap.put(userId, canForwardToProfileIds.contains(userId));
618                 }
619             }
620         }
621 
622         @SuppressLint("NewApi")
isCrossProfileContentSharingStrategyDelegatedFromParent( UserHandle userHandle)623         private boolean isCrossProfileContentSharingStrategyDelegatedFromParent(
624                 UserHandle userHandle) {
625             if (mUserManager == null) {
626                 Log.e(TAG, "can not obtain user manager");
627                 return false;
628             }
629             UserProperties userProperties = mUserManager.getUserProperties(userHandle);
630             if (java.util.Objects.equals(userProperties, null)) {
631                 Log.e(TAG, "can not obtain user properties");
632                 return false;
633             }
634 
635             return userProperties.getCrossProfileContentSharingStrategy()
636                     == UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT;
637         }
638 
getCanForwardToProfileIdMapPreV(Intent intent)639         private void getCanForwardToProfileIdMapPreV(Intent intent) {
640             // There only two profiles pre V
641             List<UserId> userIds = getUserIds();
642             for (UserId userId : userIds) {
643                 synchronized (mCanFrowardToProfileIdMap) {
644                     if (mCurrentUser.equals(userId)) {
645                         mCanFrowardToProfileIdMap.put(userId, true);
646                     } else {
647                         mCanFrowardToProfileIdMap.put(userId,
648                                 CrossProfileUtils.getCrossProfileResolveInfo(
649                                         mCurrentUser, mContext.getPackageManager(), intent,
650                                         mContext, mConfigStore.isPrivateSpaceInDocsUIEnabled())
651                                         != null);
652                     }
653                 }
654             }
655         }
656 
isDeviceSupported(Context context)657         private static boolean isDeviceSupported(Context context) {
658             // The feature requires Android R DocumentsContract APIs and INTERACT_ACROSS_USERS_FULL
659             // permission.
660             return VersionUtils.isAtLeastR()
661                     && context.checkSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS)
662                     == PackageManager.PERMISSION_GRANTED;
663         }
664     }
665 }
666