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