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