1 /* 2 * Copyright 2021 Google LLC 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 * https://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 package com.google.android.enterprise.connectedapps; 17 18 import android.app.admin.DevicePolicyManager; 19 import android.content.Context; 20 import android.content.pm.PackageInfo; 21 import android.content.pm.PackageManager; 22 import android.os.Build.VERSION; 23 import android.os.Build.VERSION_CODES; 24 import android.os.UserHandle; 25 import android.os.UserManager; 26 import com.google.android.enterprise.connectedapps.annotations.AvailabilityRestrictions; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.List; 30 import org.checkerframework.checker.nullness.qual.Nullable; 31 32 /** Utility methods for acting on profiles. These methods should only be used by the SDK. */ 33 class CrossProfileSDKUtilities { 34 private static boolean isRunningOnWorkProfileCached = false; 35 private static boolean isRunningOnWorkProfile = false; 36 isRunningOnWorkProfile(Context context)37 static boolean isRunningOnWorkProfile(Context context) { 38 if (!isRunningOnWorkProfileCached) { 39 calculateIsRunningOnWorkProfile(context); 40 } 41 return isRunningOnWorkProfile; 42 } 43 44 /** 45 * Set the {@code isRunningOnWorkProfile} field and return whether or not we can cache this value. 46 */ calculateIsRunningOnWorkProfile(Context context)47 private static void calculateIsRunningOnWorkProfile(Context context) { 48 UserManager userManager = context.getSystemService(UserManager.class); 49 isRunningOnWorkProfileCached = true; // By default we cache the result of this calculation 50 51 if (VERSION.SDK_INT >= VERSION_CODES.R) { 52 isRunningOnWorkProfile = userManager.isManagedProfile(); 53 return; 54 } 55 if (userManager.getUserProfiles().size() < 2) { 56 // This accounts for situations where a personal profile has management. 57 isRunningOnWorkProfile = false; 58 // we can't cache it as this case is also entered if we are on the work profile but it's not 59 // fully configured 60 isRunningOnWorkProfileCached = false; 61 return; 62 } 63 64 DevicePolicyManager devicePolicyManager = context.getSystemService(DevicePolicyManager.class); 65 PackageManager packageManager = context.getPackageManager(); 66 67 isRunningOnWorkProfile = false; 68 for (PackageInfo pkg : packageManager.getInstalledPackages(/* flags= */ 0)) { 69 if (devicePolicyManager.isProfileOwnerApp(pkg.packageName)) { 70 isRunningOnWorkProfile = true; 71 return; 72 } 73 } 74 } 75 isRunningOnPersonalProfile(Context context)76 static boolean isRunningOnPersonalProfile(Context context) { 77 return !isRunningOnWorkProfile(context); 78 } 79 80 /** 81 * Deterministically select the user to bind to. 82 * 83 * <p>This will ensure that on a device with multiple profiles, we bind to the same one 84 * consistently. 85 */ 86 @Nullable selectUserHandleToBind(Context context, List<UserHandle> userHandles)87 static UserHandle selectUserHandleToBind(Context context, List<UserHandle> userHandles) { 88 if (userHandles.isEmpty()) { 89 return null; 90 } 91 92 UserManager userManager = context.getSystemService(UserManager.class); 93 94 return Collections.min( 95 userHandles, 96 (o1, o2) -> 97 (int) 98 (userManager.getSerialNumberForUser(o1) - userManager.getSerialNumberForUser(o2))); 99 } 100 101 /** Filter out users according to the passed {@link AvailabilityRestrictions}. */ filterUsersByAvailabilityRestrictions( Context context, List<UserHandle> userHandles, AvailabilityRestrictions availabilityRestrictions)102 static List<UserHandle> filterUsersByAvailabilityRestrictions( 103 Context context, 104 List<UserHandle> userHandles, 105 AvailabilityRestrictions availabilityRestrictions) { 106 List<UserHandle> filteredUserHandles = new ArrayList<>(); 107 UserManager userManager = context.getSystemService(UserManager.class); 108 109 for (UserHandle userHandle : userHandles) { 110 if (!userManager.isUserRunning(userHandle)) { 111 continue; 112 } 113 if (userManager.isQuietModeEnabled(userHandle)) { 114 continue; 115 } 116 if (availabilityRestrictions.requireUnlocked && !userManager.isUserUnlocked(userHandle)) { 117 continue; 118 } 119 120 filteredUserHandles.add(userHandle); 121 } 122 123 return filteredUserHandles; 124 } 125 126 /** Should only be used during tests where the profile state may change during a single run. */ clearCache()127 static void clearCache() { 128 isRunningOnWorkProfileCached = false; 129 } 130 CrossProfileSDKUtilities()131 private CrossProfileSDKUtilities() {} 132 } 133