• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.intentresolver;
18 
19 import android.annotation.Nullable;
20 import android.app.Activity;
21 import android.app.ActivityManager;
22 import android.os.UserHandle;
23 import android.os.UserManager;
24 
25 /**
26  * Helper class to precompute the (immutable) designations of various user handles in the system
27  * that may contribute to the current Sharesheet session.
28  */
29 public final class AnnotatedUserHandles {
30     /** The user id of the app that started the share activity. */
31     public final int userIdOfCallingApp;
32 
33     /**
34      * The {@link UserHandle} that launched Sharesheet.
35      * TODO: I believe this would always be the handle corresponding to {@code userIdOfCallingApp}
36      * except possibly if the caller used {@link Activity#startActivityAsUser()} to launch
37      * Sharesheet as a different user than they themselves were running as. Verify and document.
38      */
39     public final UserHandle userHandleSharesheetLaunchedAs;
40 
41     /**
42      * The {@link UserHandle} that owns the "personal tab" in a tabbed share UI (or the *only* 'tab'
43      * in a non-tabbed UI).
44      *
45      * This is never a work or clone user, but may either be the root user (0) or a "secondary"
46      * multi-user profile (i.e., one that's not root, work, nor clone). This is a "secondary"
47      * profile only when that user is the active "foreground" user.
48      *
49      * In the current implementation, we can assert that this is the root user (0) any time we
50      * display a tabbed UI (i.e., any time `workProfileUserHandle` is non-null), or any time that we
51      * have a clone profile. This note is only provided for informational purposes; clients should
52      * avoid making any reliances on that assumption.
53      */
54     public final UserHandle personalProfileUserHandle;
55 
56     /**
57      * The {@link UserHandle} that owns the "work tab" in a tabbed share UI. This is (an arbitrary)
58      * one of the "managed" profiles associated with {@link personalProfileUserHandle}.
59      */
60     @Nullable
61     public final UserHandle workProfileUserHandle;
62 
63     /**
64      * The {@link UserHandle} of the clone profile belonging to {@link personalProfileUserHandle}.
65      */
66     @Nullable
67     public final UserHandle cloneProfileUserHandle;
68 
69     /**
70      * The "tab owner" user handle (i.e., either {@link personalProfileUserHandle} or
71      * {@link workProfileUserHandle}) that either matches or owns the profile of the
72      * {@link userHandleSharesheetLaunchedAs}.
73      *
74      * In the current implementation, we can assert that this is the same as
75      * `userHandleSharesheetLaunchedAs` except when the latter is the clone profile; then this is
76      * the "personal" profile owning that clone profile (which we currently know must belong to
77      * user 0, but clients should avoid making any reliances on that assumption).
78      */
79     public final UserHandle tabOwnerUserHandleForLaunch;
80 
AnnotatedUserHandles(Activity forShareActivity)81     public AnnotatedUserHandles(Activity forShareActivity) {
82         userIdOfCallingApp = forShareActivity.getLaunchedFromUid();
83         if ((userIdOfCallingApp < 0) || UserHandle.isIsolated(userIdOfCallingApp)) {
84             throw new SecurityException("Can't start a resolver from uid " + userIdOfCallingApp);
85         }
86 
87         // TODO: integrate logic for `ResolverActivity.EXTRA_CALLING_USER`.
88         userHandleSharesheetLaunchedAs = UserHandle.of(UserHandle.myUserId());
89 
90         personalProfileUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
91 
92         UserManager userManager = forShareActivity.getSystemService(UserManager.class);
93         workProfileUserHandle = getWorkProfileForUser(userManager, personalProfileUserHandle);
94         cloneProfileUserHandle = getCloneProfileForUser(userManager, personalProfileUserHandle);
95 
96         tabOwnerUserHandleForLaunch = (userHandleSharesheetLaunchedAs == workProfileUserHandle)
97                 ? workProfileUserHandle : personalProfileUserHandle;
98     }
99 
100     @Nullable
getWorkProfileForUser( UserManager userManager, UserHandle profileOwnerUserHandle)101     private static UserHandle getWorkProfileForUser(
102             UserManager userManager, UserHandle profileOwnerUserHandle) {
103         return userManager.getProfiles(profileOwnerUserHandle.getIdentifier()).stream()
104                 .filter(info -> info.isManagedProfile()).findFirst()
105                 .map(info -> info.getUserHandle()).orElse(null);
106     }
107 
108     @Nullable
getCloneProfileForUser( UserManager userManager, UserHandle profileOwnerUserHandle)109     private static UserHandle getCloneProfileForUser(
110             UserManager userManager, UserHandle profileOwnerUserHandle) {
111         return null;  // Not yet supported in framework.
112     }
113 }
114