1 /*
2  * Copyright 2014, 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.managedprovisioning.common;
18 
19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE;
23 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
24 import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
25 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
26 import static java.nio.charset.StandardCharsets.UTF_8;
27 
28 import android.accounts.Account;
29 import android.accounts.AccountManager;
30 import android.accounts.AccountManagerFuture;
31 import android.accounts.AuthenticatorException;
32 import android.accounts.OperationCanceledException;
33 import android.app.admin.DevicePolicyManager;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.pm.ActivityInfo;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.IPackageManager;
40 import android.content.pm.PackageInfo;
41 import android.content.pm.PackageManager;
42 import android.content.pm.PackageManager.NameNotFoundException;
43 import android.content.pm.ResolveInfo;
44 import android.content.pm.UserInfo;
45 import android.graphics.Color;
46 import android.net.ConnectivityManager;
47 import android.net.NetworkInfo;
48 import android.net.wifi.WifiManager;
49 import android.nfc.NdefMessage;
50 import android.nfc.NdefRecord;
51 import android.nfc.NfcAdapter;
52 import android.os.Build;
53 import android.os.Bundle;
54 import android.os.Parcelable;
55 import android.os.Process;
56 import android.os.RemoteException;
57 import android.os.ServiceManager;
58 import android.os.SystemProperties;
59 import android.os.UserHandle;
60 import android.os.UserManager;
61 import android.os.storage.StorageManager;
62 import android.provider.Settings.Global;
63 import android.provider.Settings.Secure;
64 import android.text.TextUtils;
65 import android.util.Base64;
66 
67 import java.io.IOException;
68 import java.lang.Integer;
69 import java.lang.Math;
70 import java.lang.String;
71 import java.nio.charset.StandardCharsets;
72 import java.util.HashSet;
73 import java.util.List;
74 import java.util.Set;
75 import java.util.concurrent.TimeUnit;
76 
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.managedprovisioning.FinalizationActivity;
79 import com.android.managedprovisioning.ProvisionLogger;
80 import com.android.managedprovisioning.TrampolineActivity;
81 import com.android.managedprovisioning.model.ProvisioningParams;
82 import com.android.managedprovisioning.model.PackageDownloadInfo;
83 
84 /**
85  * Class containing various auxiliary methods.
86  */
87 public class Utils {
88     private static final int ACCOUNT_COPY_TIMEOUT_SECONDS = 60 * 3;  // 3 minutes
89 
90     private static final int THRESHOLD_BRIGHT_COLOR = 160; // A color needs a brightness of at least
91     // this value to be considered bright. (brightness being between 0 and 255).
Utils()92     public Utils() {}
93 
94     /**
95      * Returns the currently installed system apps on a given user.
96      *
97      * <p>Calls into the {@link IPackageManager} to retrieve all installed packages on the given
98      * user and returns the package names of all system apps.
99      *
100      * @param ipm an {@link IPackageManager} object
101      * @param userId the id of the user we are interested in
102      */
getCurrentSystemApps(IPackageManager ipm, int userId)103     public Set<String> getCurrentSystemApps(IPackageManager ipm, int userId) {
104         Set<String> apps = new HashSet<String>();
105         List<ApplicationInfo> aInfos = null;
106         try {
107             aInfos = ipm.getInstalledApplications(
108                     PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList();
109         } catch (RemoteException neverThrown) {
110             ProvisionLogger.loge("This should not happen.", neverThrown);
111         }
112         for (ApplicationInfo aInfo : aInfos) {
113             if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
114                 apps.add(aInfo.packageName);
115             }
116         }
117         return apps;
118     }
119 
120     /**
121      * Disables a given component in a given user.
122      *
123      * @param toDisable the component that should be disabled
124      * @param userId the id of the user where the component should be disabled.
125      */
disableComponent(ComponentName toDisable, int userId)126     public void disableComponent(ComponentName toDisable, int userId) {
127         setComponentEnabledSetting(
128                 IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
129                 toDisable,
130                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
131                 userId);
132     }
133 
134     /**
135      * Enables a given component in a given user.
136      *
137      * @param toEnable the component that should be enabled
138      * @param userId the id of the user where the component should be disabled.
139      */
enableComponent(ComponentName toEnable, int userId)140     public void enableComponent(ComponentName toEnable, int userId) {
141         setComponentEnabledSetting(
142                 IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
143                 toEnable,
144                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
145                 userId);
146     }
147 
148     /**
149      * Disables a given component in a given user.
150      *
151      * @param ipm an {@link IPackageManager} object
152      * @param toDisable the component that should be disabled
153      * @param userId the id of the user where the component should be disabled.
154      */
155     @VisibleForTesting
setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable, int enabledSetting, int userId)156     void setComponentEnabledSetting(IPackageManager ipm, ComponentName toDisable,
157             int enabledSetting, int userId) {
158         try {
159             ipm.setComponentEnabledSetting(toDisable,
160                     enabledSetting, PackageManager.DONT_KILL_APP,
161                     userId);
162         } catch (RemoteException neverThrown) {
163             ProvisionLogger.loge("This should not happen.", neverThrown);
164         } catch (Exception e) {
165             ProvisionLogger.logw("Component not found, not changing enabled setting: "
166                 + toDisable.toShortString());
167         }
168     }
169 
170     /**
171      * Check the validity of the admin component name supplied, or try to infer this componentName
172      * from the package.
173      *
174      * We are supporting lookup by package name for legacy reasons.
175      *
176      * If mdmComponentName is supplied (not null):
177      * mdmPackageName is ignored.
178      * Check that the package of mdmComponentName is installed, that mdmComponentName is a
179      * receiver in this package, and return it. The receiver can be in disabled state.
180      *
181      * Otherwise:
182      * mdmPackageName must be supplied (not null).
183      * Check that this package is installed, try to infer a potential device admin in this package,
184      * and return it.
185      */
186     // TODO: Add unit tests
findDeviceAdmin(String mdmPackageName, ComponentName mdmComponentName, Context c)187     public ComponentName findDeviceAdmin(String mdmPackageName,
188             ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException {
189         if (mdmComponentName != null) {
190             mdmPackageName = mdmComponentName.getPackageName();
191         }
192         if (mdmPackageName == null) {
193             throw new IllegalProvisioningArgumentException("Neither the package name nor the"
194                     + " component name of the admin are supplied");
195         }
196         PackageInfo pi;
197         try {
198             pi = c.getPackageManager().getPackageInfo(mdmPackageName,
199                     PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS);
200         } catch (NameNotFoundException e) {
201             throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName
202                     + " is not installed. ", e);
203         }
204         if (mdmComponentName != null) {
205             // If the component was specified in the intent: check that it is in the manifest.
206             checkAdminComponent(mdmComponentName, pi);
207             return mdmComponentName;
208         } else {
209             // Otherwise: try to find a potential device admin in the manifest.
210             return findDeviceAdminInPackage(mdmPackageName, pi);
211         }
212     }
213 
214     /**
215      * Verifies that an admin component is part of a given package.
216      *
217      * @param mdmComponentName the admin component to be checked
218      * @param pi the {@link PackageInfo} of the package to be checked.
219      *
220      * @throws IllegalProvisioningArgumentException if the given component is not part of the
221      *         package
222      */
checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)223     private void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)
224             throws IllegalProvisioningArgumentException{
225         for (ActivityInfo ai : pi.receivers) {
226             if (mdmComponentName.getClassName().equals(ai.name)) {
227                 return;
228             }
229         }
230         throw new IllegalProvisioningArgumentException("The component " + mdmComponentName
231                 + " cannot be found");
232     }
233 
findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)234     private ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)
235             throws IllegalProvisioningArgumentException {
236         ComponentName mdmComponentName = null;
237         for (ActivityInfo ai : pi.receivers) {
238             if (!TextUtils.isEmpty(ai.permission) &&
239                     ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) {
240                 if (mdmComponentName != null) {
241                     throw new IllegalProvisioningArgumentException("There are several "
242                             + "device admins in " + mdmPackageName + " but no one in specified");
243                 } else {
244                     mdmComponentName = new ComponentName(mdmPackageName, ai.name);
245                 }
246             }
247         }
248         if (mdmComponentName == null) {
249             throw new IllegalProvisioningArgumentException("There are no device admins in"
250                     + mdmPackageName);
251         }
252         return mdmComponentName;
253     }
254 
255     /**
256      * Returns whether the current user is the system user.
257      */
isCurrentUserSystem()258     public boolean isCurrentUserSystem() {
259         return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
260     }
261 
262     /**
263      * Returns whether the device is currently managed.
264      */
isDeviceManaged(Context context)265     public boolean isDeviceManaged(Context context) {
266         DevicePolicyManager dpm =
267                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
268         return dpm.isDeviceManaged();
269     }
270 
271     /**
272      * Returns whether the calling user is a managed profile.
273      */
isManagedProfile(Context context)274     public boolean isManagedProfile(Context context) {
275         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
276         UserInfo user = um.getUserInfo(UserHandle.myUserId());
277         return user != null ? user.isManagedProfile() : false;
278     }
279 
280     /**
281      * Returns true if the given package requires an update.
282      *
283      * <p>There are two cases where an update is required:
284      * 1. The package is not currently present on the device.
285      * 2. The package is present, but the version is below the minimum supported version.
286      *
287      * @param packageName the package to be checked for updates
288      * @param minSupportedVersion the minimum supported version
289      * @param context a {@link Context} object
290      */
packageRequiresUpdate(String packageName, int minSupportedVersion, Context context)291     public boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
292             Context context) {
293         try {
294             PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
295             // Always download packages if no minimum version given.
296             if (minSupportedVersion != PackageDownloadInfo.DEFAULT_MINIMUM_VERSION
297                     && packageInfo.versionCode >= minSupportedVersion) {
298                 return false;
299             }
300         } catch (NameNotFoundException e) {
301             // Package not on device.
302         }
303 
304         return true;
305     }
306 
307     /**
308      * Transforms a string into a byte array.
309      *
310      * @param s the string to be transformed
311      */
stringToByteArray(String s)312     public byte[] stringToByteArray(String s)
313         throws NumberFormatException {
314         try {
315             return Base64.decode(s, Base64.URL_SAFE);
316         } catch (IllegalArgumentException e) {
317             throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded.");
318         }
319     }
320 
321     /**
322      * Transforms a byte array into a string.
323      *
324      * @param bytes the byte array to be transformed
325      */
byteArrayToString(byte[] bytes)326     public String byteArrayToString(byte[] bytes) {
327         return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
328     }
329 
330     /**
331      * Sets user setup complete on a given user.
332      *
333      * <p>This will set USER_SETUP_COMPLETE to 1 on the given user.
334      */
markUserSetupComplete(Context context, int userId)335     public void markUserSetupComplete(Context context, int userId) {
336         ProvisionLogger.logd("Setting USER_SETUP_COMPLETE to 1 for user " + userId);
337         Secure.putIntForUser(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, userId);
338     }
339 
340     /**
341      * Returns whether USER_SETUP_COMPLETE is set on the calling user.
342      */
isUserSetupCompleted(Context context)343     public boolean isUserSetupCompleted(Context context) {
344         return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0;
345     }
346 
347     /**
348      * Returns whether DEVICE_PROVISIONED is set.
349      */
isDeviceProvisioned(Context context)350     public boolean isDeviceProvisioned(Context context) {
351         return Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;
352     }
353 
354     /**
355      * Set the current users userProvisioningState depending on the following factors:
356      * <ul>
357      *     <li>We're setting up a managed-profile - need to set state on two users.</li>
358      *     <li>User-setup has previously been completed or not - skip states relating to
359      *     communicating with setup-wizard</li>
360      *     <li>DPC requested we skip the rest of setup-wizard.</li>
361      * </ul>
362      *
363      * @param context a {@link Context} object
364      * @param params configuration for current provisioning attempt
365      */
366     // TODO: Add unit tests
markUserProvisioningStateInitiallyDone(Context context, ProvisioningParams params)367     public void markUserProvisioningStateInitiallyDone(Context context,
368             ProvisioningParams params) {
369         int currentUserId = UserHandle.myUserId();
370         int managedProfileUserId = UserHandle.USER_NULL;
371         DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
372 
373         // new provisioning state for current user, if non-null
374         Integer newState = null;
375          // New provisioning state for managed-profile of current user, if non-null.
376         Integer newProfileState = null;
377 
378         boolean userSetupCompleted = isUserSetupCompleted(context);
379         if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
380             // Managed profiles are a special case as two users are involved.
381             managedProfileUserId = getManagedProfile(context).getIdentifier();
382             if (userSetupCompleted) {
383                 // SUW on current user is complete, so nothing much to do beyond indicating we're
384                 // all done.
385                 newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
386             } else {
387                 // We're still in SUW, so indicate that a managed-profile was setup on current user,
388                 // and that we're awaiting finalization on both.
389                 newState = DevicePolicyManager.STATE_USER_PROFILE_COMPLETE;
390                 newProfileState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE;
391             }
392         } else if (userSetupCompleted) {
393             // User setup was previously completed this is an unexpected case.
394             ProvisionLogger.logw("user_setup_complete set, but provisioning was started");
395         } else if (params.skipUserSetup) {
396             // DPC requested setup-wizard is skipped, indicate this to SUW.
397             newState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE;
398         } else {
399             // DPC requested setup-wizard is not skipped, indicate this to SUW.
400             newState = DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE;
401         }
402 
403         if (newState != null) {
404             setUserProvisioningState(dpm, newState, currentUserId);
405         }
406         if (newProfileState != null) {
407             setUserProvisioningState(dpm, newProfileState, managedProfileUserId);
408         }
409         if (!userSetupCompleted) {
410             // We expect a PROVISIONING_FINALIZATION intent to finish setup if we're still in
411             // user-setup.
412             FinalizationActivity.storeProvisioningParams(context, params);
413         }
414     }
415 
416     /**
417      * Finalize the current users userProvisioningState depending on the following factors:
418      * <ul>
419      *     <li>We're setting up a managed-profile - need to set state on two users.</li>
420      * </ul>
421      *
422      * @param context a {@link Context} object
423      * @param params configuration for current provisioning attempt - if null (because
424      *               ManagedProvisioning wasn't used for first phase of provisioning) aassumes we
425      *               can just mark current user as being in finalized provisioning state
426      */
427     // TODO: Add unit tests
markUserProvisioningStateFinalized(Context context, ProvisioningParams params)428     public void markUserProvisioningStateFinalized(Context context,
429             ProvisioningParams params) {
430         int currentUserId = UserHandle.myUserId();
431         int managedProfileUserId = -1;
432         DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
433         Integer newState = null;
434         Integer newProfileState = null;
435 
436         if (params != null && params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
437             // Managed profiles are a special case as two users are involved.
438             managedProfileUserId = getManagedProfile(context).getIdentifier();
439 
440             newState = DevicePolicyManager.STATE_USER_UNMANAGED;
441             newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
442         } else {
443             newState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
444         }
445 
446         if (newState != null) {
447             setUserProvisioningState(dpm, newState, currentUserId);
448         }
449         if (newProfileState != null) {
450             setUserProvisioningState(dpm, newProfileState, managedProfileUserId);
451         }
452     }
453 
setUserProvisioningState(DevicePolicyManager dpm, int state, int userId)454     private void setUserProvisioningState(DevicePolicyManager dpm, int state, int userId) {
455         ProvisionLogger.logi("Setting userProvisioningState for user " + userId + " to: " + state);
456         dpm.setUserProvisioningState(state, userId);
457     }
458 
459     /**
460      * Returns the first existing managed profile if any present, null otherwise.
461      *
462      * <p>Note that we currently only support one managed profile per device.
463      */
464     // TODO: Add unit tests
getManagedProfile(Context context)465     public UserHandle getManagedProfile(Context context) {
466         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
467         int currentUserId = userManager.getUserHandle();
468         List<UserInfo> userProfiles = userManager.getProfiles(currentUserId);
469         for (UserInfo profile : userProfiles) {
470             if (profile.isManagedProfile()) {
471                 return new UserHandle(profile.id);
472             }
473         }
474         return null;
475     }
476 
477     /**
478      * Returns the user id of an already existing managed profile or -1 if none exists.
479      */
480     // TODO: Add unit tests
alreadyHasManagedProfile(Context context)481     public int alreadyHasManagedProfile(Context context) {
482         UserHandle managedUser = getManagedProfile(context);
483         if (managedUser != null) {
484             return managedUser.getIdentifier();
485         } else {
486             return -1;
487         }
488     }
489 
490     /**
491      * Removes an account.
492      *
493      * <p>This removes the given account from the calling user's list of accounts.
494      *
495      * @param context a {@link Context} object
496      * @param account the account to be removed
497      */
498     // TODO: Add unit tests
removeAccount(Context context, Account account)499     public void removeAccount(Context context, Account account) {
500         try {
501             AccountManager accountManager =
502                     (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
503             AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
504                     null, null /* callback */, null /* handler */);
505             // Block to get the result of the removeAccount operation
506             if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
507                 ProvisionLogger.logw("Account removed from the primary user.");
508             } else {
509                 Intent removeIntent = (Intent) bundle.getResult().getParcelable(
510                         AccountManager.KEY_INTENT);
511                 if (removeIntent != null) {
512                     ProvisionLogger.logi("Starting activity to remove account");
513                     TrampolineActivity.startActivity(context, removeIntent);
514                 } else {
515                     ProvisionLogger.logw("Could not remove account from the primary user.");
516                 }
517             }
518         } catch (OperationCanceledException | AuthenticatorException | IOException e) {
519             ProvisionLogger.logw("Exception removing account from the primary user.", e);
520         }
521     }
522 
523     /**
524      * Copies an account to a given user.
525      *
526      * <p>Copies a given account form {@code sourceUser} to {@code targetUser}. This call is
527      * blocking until the operation has succeeded. If within a timeout the account hasn't been
528      * successfully copied to the new user, we give up.
529      *
530      * @param context a {@link Context} object
531      * @param accountToMigrate the account to be migrated
532      * @param sourceUser the {@link UserHandle} of the user to copy from
533      * @param targetUser the {@link UserHandle} of the user to copy to
534      * @return whether account migration successfully completed
535      */
maybeCopyAccount(Context context, Account accountToMigrate, UserHandle sourceUser, UserHandle targetUser)536     public boolean maybeCopyAccount(Context context, Account accountToMigrate,
537             UserHandle sourceUser, UserHandle targetUser) {
538         if (accountToMigrate == null) {
539             ProvisionLogger.logd("No account to migrate.");
540             return false;
541         }
542         if (sourceUser.equals(targetUser)) {
543             ProvisionLogger.loge("sourceUser and targetUser are the same, won't migrate account.");
544             return false;
545         }
546         ProvisionLogger.logd("Attempting to copy account from " + sourceUser + " to " + targetUser);
547         try {
548             AccountManager accountManager =
549                     (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
550             boolean copySucceeded = accountManager.copyAccountToUser(
551                     accountToMigrate,
552                     sourceUser,
553                     targetUser,
554                     /* callback= */ null, /* handler= */ null)
555                     .getResult(ACCOUNT_COPY_TIMEOUT_SECONDS, TimeUnit.SECONDS);
556             if (copySucceeded) {
557                 ProvisionLogger.logi("Copied account to " + targetUser);
558                 return true;
559             } else {
560                 ProvisionLogger.loge("Could not copy account to " + targetUser);
561             }
562         } catch (OperationCanceledException | AuthenticatorException | IOException e) {
563             ProvisionLogger.loge("Exception copying account to " + targetUser, e);
564         }
565         return false;
566     }
567 
568     /**
569      * Returns whether FRP is supported on the device.
570      */
isFrpSupported(Context context)571     public boolean isFrpSupported(Context context) {
572         Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
573         return pdbManager != null;
574     }
575 
576     /**
577      * Translates a given managed provisioning intent to its corresponding provisioning flow, using
578      * the action from the intent.
579      *
580      * <p/>This is necessary because, unlike other provisioning actions which has 1:1 mapping, there
581      * are multiple actions that can trigger the device owner provisioning flow. This includes
582      * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link ACTION_NDEF_DISCOVERED} and
583      * {@link ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. These 3 actions are equivalent
584      * excepts they are sent from a different source.
585      *
586      * @return the appropriate DevicePolicyManager declared action for the given incoming intent.
587      * @throws IllegalProvisioningArgumentException if intent is malformed
588      */
589     // TODO: Add unit tests
mapIntentToDpmAction(Intent intent)590     public String mapIntentToDpmAction(Intent intent)
591             throws IllegalProvisioningArgumentException {
592         if (intent == null || intent.getAction() == null) {
593             throw new IllegalProvisioningArgumentException("Null intent action.");
594         }
595 
596         // Map the incoming intent to a DevicePolicyManager.ACTION_*, as there is a N:1 mapping in
597         // some cases.
598         String dpmProvisioningAction;
599         switch (intent.getAction()) {
600             // Trivial cases.
601             case ACTION_PROVISION_MANAGED_DEVICE:
602             case ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
603             case ACTION_PROVISION_MANAGED_USER:
604             case ACTION_PROVISION_MANAGED_PROFILE:
605                 dpmProvisioningAction = intent.getAction();
606                 break;
607 
608             // NFC cases which need to take mime-type into account.
609             case ACTION_NDEF_DISCOVERED:
610                 String mimeType = intent.getType();
611                 switch (mimeType) {
612                     case MIME_TYPE_PROVISIONING_NFC:
613                         dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE;
614                         break;
615 
616                     default:
617                         throw new IllegalProvisioningArgumentException(
618                                 "Unknown NFC bump mime-type: " + mimeType);
619                 }
620                 break;
621 
622             // Device owner provisioning from a trusted app.
623             // TODO (b/27217042): review for new management modes in split system-user model
624             case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE:
625                 dpmProvisioningAction = ACTION_PROVISION_MANAGED_DEVICE;
626                 break;
627 
628             default:
629                 throw new IllegalProvisioningArgumentException("Unknown intent action "
630                         + intent.getAction());
631         }
632         return dpmProvisioningAction;
633     }
634 
635     /**
636      * Sends an intent to trigger a factory reset.
637      */
638     // TODO: Move the FR intent into a Globals class.
sendFactoryResetBroadcast(Context context, String reason)639     public void sendFactoryResetBroadcast(Context context, String reason) {
640         Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
641         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
642         intent.putExtra(Intent.EXTRA_REASON, reason);
643         context.sendBroadcast(intent);
644     }
645 
646     /**
647      * Returns whether the given provisioning action is a profile owner action.
648      */
649     // TODO: Move the list of device owner actions into a Globals class.
isProfileOwnerAction(String action)650     public final boolean isProfileOwnerAction(String action) {
651         return action.equals(ACTION_PROVISION_MANAGED_PROFILE)
652                 || action.equals(ACTION_PROVISION_MANAGED_USER);
653     }
654 
655     /**
656      * Returns whether the given provisioning action is a device owner action.
657      */
658     // TODO: Move the list of device owner actions into a Globals class.
isDeviceOwnerAction(String action)659     public final boolean isDeviceOwnerAction(String action) {
660         return action.equals(ACTION_PROVISION_MANAGED_DEVICE)
661                 || action.equals(ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE);
662     }
663 
664     /**
665      * Returns whether the device currently has connectivity.
666      */
isConnectedToNetwork(Context context)667     public boolean isConnectedToNetwork(Context context) {
668         NetworkInfo info = getActiveNetworkInfo(context);
669         return info != null && info.isConnected();
670     }
671 
672     /**
673      * Returns whether the device is currently connected to a wifi.
674      */
isConnectedToWifi(Context context)675     public boolean isConnectedToWifi(Context context) {
676         NetworkInfo info = getActiveNetworkInfo(context);
677         return info != null
678                 && info.isConnected()
679                 && info.getType() == ConnectivityManager.TYPE_WIFI;
680     }
681 
getActiveNetworkInfo(Context context)682     private NetworkInfo getActiveNetworkInfo(Context context) {
683         ConnectivityManager cm =
684                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
685         if (cm != null) {
686             return cm.getActiveNetworkInfo();
687         }
688         return null;
689     }
690 
691     /**
692      * Returns whether encryption is required on this device.
693      *
694      * <p>Encryption is required if the device is not currently encrypted and the persistent
695      * system flag {@code persist.sys.no_req_encrypt} is not set.
696      */
isEncryptionRequired()697     public boolean isEncryptionRequired() {
698         return !isPhysicalDeviceEncrypted()
699                 && !SystemProperties.getBoolean("persist.sys.no_req_encrypt", false);
700     }
701 
702     /**
703      * Returns whether the device is currently encrypted.
704      */
isPhysicalDeviceEncrypted()705     public boolean isPhysicalDeviceEncrypted() {
706         return StorageManager.isEncrypted();
707     }
708 
709     /**
710      * Returns the wifi pick intent.
711      */
712     // TODO: Move this intent into a Globals class.
getWifiPickIntent()713     public Intent getWifiPickIntent() {
714         Intent wifiIntent = new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK);
715         wifiIntent.putExtra("extra_prefs_show_button_bar", true);
716         wifiIntent.putExtra("wifi_enable_next_on_connect", true);
717         return wifiIntent;
718     }
719 
720     /**
721      * Returns whether the device has a split system user.
722      *
723      * <p>Split system user means that user 0 is system only and all meat users are separate from
724      * the system user.
725      */
isSplitSystemUser()726     public boolean isSplitSystemUser() {
727         return UserManager.isSplitSystemUser();
728     }
729 
730     /**
731      * Returns whether the currently chosen launcher supports managed profiles.
732      *
733      * <p>A launcher is deemed to support managed profiles when its target API version is at least
734      * {@link Build.VERSION_CODES#LOLLIPOP}.
735      */
currentLauncherSupportsManagedProfiles(Context context)736     public boolean currentLauncherSupportsManagedProfiles(Context context) {
737         Intent intent = new Intent(Intent.ACTION_MAIN);
738         intent.addCategory(Intent.CATEGORY_HOME);
739 
740         PackageManager pm = context.getPackageManager();
741         ResolveInfo launcherResolveInfo = pm.resolveActivity(intent,
742                 PackageManager.MATCH_DEFAULT_ONLY);
743         if (launcherResolveInfo == null) {
744             return false;
745         }
746         try {
747             // If the user has not chosen a default launcher, then launcherResolveInfo will be
748             // referring to the resolver activity. It is fine to create a managed profile in
749             // this case since there will always be at least one launcher on the device that
750             // supports managed profile feature.
751             ApplicationInfo launcherAppInfo = pm.getApplicationInfo(
752                     launcherResolveInfo.activityInfo.packageName, 0 /* default flags */);
753             return versionNumberAtLeastL(launcherAppInfo.targetSdkVersion);
754         } catch (PackageManager.NameNotFoundException e) {
755             return false;
756         }
757     }
758 
759     /**
760      * Returns whether the given version number is at least lollipop.
761      *
762      * @param versionNumber the version number to be verified.
763      */
versionNumberAtLeastL(int versionNumber)764     private boolean versionNumberAtLeastL(int versionNumber) {
765         return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
766     }
767 
isBrightColor(int color)768     public boolean isBrightColor(int color) {
769         // we're using the brightness formula: (r * 299 + g * 587 + b * 144) / 1000
770         return Color.red(color) * 299 + Color.green(color) * 587 + Color.blue(color) * 114
771                 >= 1000 * THRESHOLD_BRIGHT_COLOR;
772     }
773 }
774