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