1 /*
2  * Copyright (C) 2021 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 package com.android.car.settings.enterprise;
17 
18 import android.annotation.Nullable;
19 import android.annotation.UserIdInt;
20 import android.app.admin.DeviceAdminInfo;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ActivityInfo;
26 import android.content.pm.PackageManager;
27 import android.os.Bundle;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 
31 import com.android.car.settings.common.Logger;
32 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
33 import com.android.settingslib.RestrictedLockUtilsInternal;
34 
35 import org.xmlpull.v1.XmlPullParserException;
36 
37 import java.io.IOException;
38 import java.util.List;
39 
40 /**
41  * Generic helper methods for this package.
42  */
43 public final class EnterpriseUtils {
44 
45     private static final Logger LOG = new Logger(EnterpriseUtils.class);
46     // TODO: ideally, we should not create a special user restriction other than what are
47     // defined in UserManager.
48     public static final String DISABLED_INPUT_METHOD = "disabled-input-method";
49 
50     /**
51      * Gets the active admin for the given package.
52      */
53     @Nullable
getAdminWithinPackage(Context context, String packageName)54     public static ComponentName getAdminWithinPackage(Context context, String packageName) {
55         List<ComponentName> admins = context.getSystemService(DevicePolicyManager.class)
56                 .getActiveAdmins();
57         if (admins == null) {
58             LOG.v("getAdminWithinPackage(): no active admins on context");
59             return null;
60         }
61         return admins.stream().filter(i -> i.getPackageName().equals(packageName)).findAny()
62                 .orElse(null);
63     }
64 
65     /**
66      * Gets the active admin info for the given admin .
67      */
68     @Nullable
getDeviceAdminInfo(Context context, ComponentName admin)69     public static DeviceAdminInfo getDeviceAdminInfo(Context context, ComponentName admin) {
70         LOG.d("getDeviceAdminInfo()(): " + admin.flattenToShortString());
71 
72         ActivityInfo ai;
73         try {
74             ai = context.getPackageManager().getReceiverInfo(admin, PackageManager.GET_META_DATA);
75         } catch (PackageManager.NameNotFoundException e) {
76             LOG.v("Unable to get activity info for " + admin.flattenToShortString() + ": " + e);
77             return null;
78         }
79 
80         try {
81             return new DeviceAdminInfo(context, ai);
82         } catch (XmlPullParserException | IOException e) {
83             LOG.v("Unable to retrieve device policy for " + admin.flattenToShortString() + ": ",
84                     e);
85             return null;
86         }
87     }
88 
89     /**
90      * Checks whether current user has the flag {@link UserManager.FLAG_DEMO}.
91      */
isDemoUser(Context context)92     public static boolean isDemoUser(Context context) {
93         return UserManager.isDeviceInDemoMode(context)
94                 && getUserManager(context).isDemoUser();
95     }
96 
97     /**
98      * Checks whether current user has the flag {@link UserManager.FLAG_ADMIN}.
99      */
isAdminUser(Context context)100     public static boolean isAdminUser(Context context) {
101         return getUserManager(context).isAdminUser();
102     }
103 
104     /**
105      * Checks whether the restriction is set on the current user by device owner / profile owners
106      * but not by {@link UserManager}.
107      *
108      * <p>This includes restriction set on device owner but current user has affiliated profile
109      * owner.
110      */
hasUserRestrictionByDpm(Context context, String restriction)111     public static boolean hasUserRestrictionByDpm(Context context, String restriction) {
112         if (hasUserRestrictionByUm(context, restriction)) {
113             return false;
114         }
115         return getUserManager(context).hasUserRestriction(restriction);
116     }
117 
118     /**
119      * Checks whether there are restrictions set via {@link UserManager} which doesn't include
120      * restrictions set by device owner / profile owners.
121      */
hasUserRestrictionByUm(Context context, String restriction)122     public static boolean hasUserRestrictionByUm(Context context, String restriction) {
123         return getUserManager(context)
124                 .hasBaseUserRestriction(restriction, UserHandle.of(context.getUserId()));
125     }
126 
127     /**
128      * Checks whether device owner is set on the device.
129      */
hasDeviceOwner(Context context)130     public static boolean hasDeviceOwner(Context context) {
131         DevicePolicyManager dpm = getDevicePolicyManager(context);
132         return dpm.isDeviceManaged() && getDeviceOwner(context) != null;
133     }
134 
135     /**
136      * Gets device owner user id on the device.
137      */
138     @UserIdInt
getDeviceOwnerUserId(Context context)139     private static int getDeviceOwnerUserId(Context context) {
140         return getDevicePolicyManager(context).getDeviceOwnerUserId();
141     }
142 
143     /**
144      * Gets device owner component on the device.
145      */
146     @Nullable
getDeviceOwner(Context context)147     private static ComponentName getDeviceOwner(Context context) {
148         return getDevicePolicyManager(context).getDeviceOwnerComponentOnAnyUser();
149     }
150 
getUserManager(Context context)151     private static UserManager getUserManager(Context context) {
152         return context.getSystemService(UserManager.class);
153     }
154 
getDevicePolicyManager(Context context)155     private static DevicePolicyManager getDevicePolicyManager(Context context) {
156         return context.getSystemService(DevicePolicyManager.class);
157     }
158 
159     /**
160      * Gets an {@code ActionDisabledByAdminDialogFragment} for the target restriction to show on
161      * the current user.
162      */
getActionDisabledByAdminDialog( Context context, String restriction)163     public static ActionDisabledByAdminDialogFragment getActionDisabledByAdminDialog(
164             Context context, String restriction) {
165         return getActionDisabledByAdminDialog(context, restriction, /* restrictedPackage= */ null);
166     }
167 
168     /**
169      * Gets an {@code ActionDisabledByAdminDialogFragment} when the input method is restricted for
170      * the current user.
171      */
getInputMethodDisabledByAdminDialog( Context context, String restriction)172     public static ActionDisabledByAdminDialogFragment getInputMethodDisabledByAdminDialog(
173             Context context, String restriction) {
174         return getActionDisabledByAdminDialog(context, restriction, /* restrictedPackage= */ null);
175     }
176 
177     /**
178      * Gets an {@code ActionDisabledByAdminDialogFragment} for the target restriction to show on
179      * the current user with additional restricted package information.
180      */
getActionDisabledByAdminDialog( Context context, String restriction, @Nullable String restrictedPackage)181     public static ActionDisabledByAdminDialogFragment getActionDisabledByAdminDialog(
182             Context context, String restriction, @Nullable String restrictedPackage) {
183         int adminUser = hasDeviceOwner(context)
184                 ? getDeviceOwnerUserId(context)
185                 : context.getUserId();
186         return ActionDisabledByAdminDialogFragment
187                 .newInstance(restriction, restrictedPackage, adminUser);
188     }
189 
190     /**
191      * Gets enforced admin information from Intent that started the
192      * {@code ActionDisabledByAdminDialogActivity}.
193      */
getEnforcedAdminFromIntent(Context context, Intent intent)194     public static EnforcedAdmin getEnforcedAdminFromIntent(Context context, Intent intent) {
195         EnforcedAdmin admin = new EnforcedAdmin(null, context.getUser());
196         if (intent == null) {
197             return admin;
198         }
199         admin.component = intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN);
200         int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, context.getUserId());
201 
202         Bundle adminDetails = null;
203         if (admin.component == null) {
204             DevicePolicyManager devicePolicyManager = getDevicePolicyManager(context);
205             admin.component = adminDetails.getParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN);
206         }
207 
208         if (intent.hasExtra(Intent.EXTRA_USER)) {
209             admin.user = intent.getParcelableExtra(Intent.EXTRA_USER);
210         } else {
211             if (adminDetails != null) {
212                 userId = adminDetails.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
213             }
214             if (userId == UserHandle.USER_NULL) {
215                 admin.user = null;
216             } else {
217                 admin.user = UserHandle.of(userId);
218             }
219         }
220         return admin;
221     }
222 
223     /**
224      * Gets {@code RestrictedLockUtils.EnforcedAdmin} for the device policy that affects
225      * current user.
226      *
227      * @param context for current user
228      * @param adminUser which can be either profile owner on current user or device owner on
229      *        headless system user
230      * @param restriction which can be user restriction or restriction policy defined
231      *        in this class
232      * @param restrictedPackage is the target package that restriction policy is set
233      * @return {@code RestrictedLockUtils.EnforcedAdmin}
234      */
getEnforcedAdmin(Context context, @UserIdInt int adminUser, String restriction, String restrictedPackage)235     public static EnforcedAdmin getEnforcedAdmin(Context context, @UserIdInt int adminUser,
236             String restriction, String restrictedPackage) {
237         EnforcedAdmin admin = null;
238         if (hasUserRestrictionByDpm(context, restriction)) {
239             admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
240                     context, restriction, context.getUserId());
241             LOG.v("getEnforcedAdmin(): " + adminUser + " restriction: " + restriction
242                     + " restrictedPackage: " + restrictedPackage);
243 
244             if (admin.component == null && context.getUserId() != adminUser) {
245                 // User restriction might be set on primary user which is user 0 as a device-wide
246                 // policy.
247                 admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
248                         context, restriction, adminUser);
249             }
250         } else if (restriction == DISABLED_INPUT_METHOD) {
251             if (restrictedPackage == null) {
252                 LOG.e("getEnforcedAdmin() for " + DISABLED_INPUT_METHOD
253                         + " fails since restrictedPackage is null");
254                 return admin;
255             }
256             admin = RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(
257                     context, restrictedPackage, context.getUserId());
258         }
259         LOG.v("getEnforcedAdmin():" + admin);
260         return admin;
261     }
262 
EnterpriseUtils()263     private EnterpriseUtils() {
264         throw new UnsupportedOperationException("Provides only static methods");
265     }
266 }
267