• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ActivityInfo;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.IPackageManager;
27 import android.content.pm.PackageInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.NameNotFoundException;
30 import android.content.pm.UserInfo;
31 import android.graphics.drawable.Drawable;
32 import android.os.AsyncTask;
33 import android.os.Binder;
34 import android.os.Bundle;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.Settings.Global;
40 import android.provider.Settings.Secure;
41 import android.text.TextUtils;
42 import android.util.Base64;
43 
44 import java.io.IOException;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Set;
48 
49 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
50 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
51 
52 import android.accounts.Account;
53 import android.accounts.AccountManager;
54 import android.accounts.AccountManagerFuture;
55 import android.accounts.AuthenticatorException;
56 import android.accounts.OperationCanceledException;
57 
58 /**
59  * Class containing various auxiliary methods.
60  */
61 public class Utils {
Utils()62     private Utils() {}
63 
getCurrentSystemApps(int userId)64     public static Set<String> getCurrentSystemApps(int userId) {
65         IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager
66                 .getService("package"));
67         Set<String> apps = new HashSet<String>();
68         List<ApplicationInfo> aInfos = null;
69         try {
70             aInfos = ipm.getInstalledApplications(
71                     PackageManager.GET_UNINSTALLED_PACKAGES, userId).getList();
72         } catch (RemoteException neverThrown) {
73             ProvisionLogger.loge("This should not happen.", neverThrown);
74         }
75         for (ApplicationInfo aInfo : aInfos) {
76             if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
77                 apps.add(aInfo.packageName);
78             }
79         }
80         return apps;
81     }
82 
disableComponent(ComponentName toDisable, int userId)83     public static void disableComponent(ComponentName toDisable, int userId) {
84         try {
85             IPackageManager ipm = IPackageManager.Stub.asInterface(ServiceManager
86                 .getService("package"));
87 
88             ipm.setComponentEnabledSetting(toDisable,
89                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP,
90                     userId);
91         } catch (RemoteException neverThrown) {
92             ProvisionLogger.loge("This should not happen.", neverThrown);
93         } catch (Exception e) {
94             ProvisionLogger.logw("Component not found, not disabling it: "
95                 + toDisable.toShortString());
96         }
97     }
98 
99     /**
100      * Exception thrown when the provisioning has failed completely.
101      *
102      * We're using a custom exception to avoid catching subsequent exceptions that might be
103      * significant.
104      */
105     public static class IllegalProvisioningArgumentException extends Exception {
IllegalProvisioningArgumentException(String message)106         public IllegalProvisioningArgumentException(String message) {
107             super(message);
108         }
109 
IllegalProvisioningArgumentException(String message, Throwable t)110         public IllegalProvisioningArgumentException(String message, Throwable t) {
111             super(message, t);
112         }
113     }
114 
115     /**
116      * Check the validity of the admin component name supplied, or try to infer this componentName
117      * from the package.
118      *
119      * We are supporting lookup by package name for legacy reasons.
120      *
121      * If mdmComponentName is supplied (not null):
122      * mdmPackageName is ignored.
123      * Check that the package of mdmComponentName is installed, that mdmComponentName is a
124      * receiver in this package, and return it.
125      *
126      * Otherwise:
127      * mdmPackageName must be supplied (not null).
128      * Check that this package is installed, try to infer a potential device admin in this package,
129      * and return it.
130      */
findDeviceAdmin(String mdmPackageName, ComponentName mdmComponentName, Context c)131     public static ComponentName findDeviceAdmin(String mdmPackageName,
132             ComponentName mdmComponentName, Context c) throws IllegalProvisioningArgumentException {
133         if (mdmComponentName != null) {
134             mdmPackageName = mdmComponentName.getPackageName();
135         }
136         if (mdmPackageName == null) {
137             throw new IllegalProvisioningArgumentException("Neither the package name nor the"
138                     + " component name of the admin are supplied");
139         }
140         PackageInfo pi;
141         try {
142             pi = c.getPackageManager().getPackageInfo(mdmPackageName,
143                     PackageManager.GET_RECEIVERS);
144         } catch (NameNotFoundException e) {
145             throw new IllegalProvisioningArgumentException("Mdm "+ mdmPackageName
146                     + " is not installed. ", e);
147         }
148         if (mdmComponentName != null) {
149             // If the component was specified in the intent: check that it is in the manifest.
150             checkAdminComponent(mdmComponentName, pi);
151             return mdmComponentName;
152         } else {
153             // Otherwise: try to find a potential device admin in the manifest.
154             return findDeviceAdminInPackage(mdmPackageName, pi);
155         }
156     }
157 
checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)158     private static void checkAdminComponent(ComponentName mdmComponentName, PackageInfo pi)
159             throws IllegalProvisioningArgumentException{
160         for (ActivityInfo ai : pi.receivers) {
161             if (mdmComponentName.getClassName().equals(ai.name)) {
162                 return;
163             }
164         }
165         throw new IllegalProvisioningArgumentException("The component " + mdmComponentName
166                 + " cannot be found");
167     }
168 
findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)169     private static ComponentName findDeviceAdminInPackage(String mdmPackageName, PackageInfo pi)
170             throws IllegalProvisioningArgumentException {
171         ComponentName mdmComponentName = null;
172         for (ActivityInfo ai : pi.receivers) {
173             if (!TextUtils.isEmpty(ai.permission) &&
174                     ai.permission.equals(android.Manifest.permission.BIND_DEVICE_ADMIN)) {
175                 if (mdmComponentName != null) {
176                     throw new IllegalProvisioningArgumentException("There are several "
177                             + "device admins in " + mdmPackageName + " but no one in specified");
178                 } else {
179                     mdmComponentName = new ComponentName(mdmPackageName, ai.name);
180                 }
181             }
182         }
183         if (mdmComponentName == null) {
184             throw new IllegalProvisioningArgumentException("There are no device admins in"
185                     + mdmPackageName);
186         }
187         return mdmComponentName;
188     }
189 
getMdmPackageInfo(PackageManager pm, String packageName)190     public static MdmPackageInfo getMdmPackageInfo(PackageManager pm, String packageName) {
191         if (packageName != null) {
192             try {
193                 ApplicationInfo ai = pm.getApplicationInfo(packageName, /* default flags */ 0);
194                 if (ai != null) {
195                     return new MdmPackageInfo(pm.getApplicationIcon(packageName),
196                             pm.getApplicationLabel(ai).toString());
197                 }
198             } catch (PackageManager.NameNotFoundException e) {
199                 // Package does not exist, ignore. Should never happen.
200                 ProvisionLogger.loge("Package does not exist. Should never happen.");
201             }
202         }
203 
204         return null;
205     }
206 
207     /**
208      * Information relating to the currently installed MDM package manager.
209      */
210     public static final class MdmPackageInfo {
211         private final Drawable packageIcon;
212         private final String appLabel;
213 
MdmPackageInfo(Drawable packageIcon, String appLabel)214         private MdmPackageInfo(Drawable packageIcon, String appLabel) {
215             this.packageIcon = packageIcon;
216             this.appLabel = appLabel;
217         }
218 
getAppLabel()219         public String getAppLabel() {
220             return appLabel;
221         }
222 
getPackageIcon()223         public Drawable getPackageIcon() {
224             return packageIcon;
225         }
226     }
227 
isCurrentUserOwner()228     public static boolean isCurrentUserOwner() {
229         return UserHandle.myUserId() == UserHandle.USER_OWNER;
230     }
231 
hasDeviceOwner(Context context)232     public static boolean hasDeviceOwner(Context context) {
233         DevicePolicyManager dpm =
234                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
235         return !TextUtils.isEmpty(dpm.getDeviceOwner());
236     }
237 
hasDeviceInitializer(Context context)238     public static boolean hasDeviceInitializer(Context context) {
239         DevicePolicyManager dpm =
240                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
241         return dpm != null && dpm.getDeviceInitializerApp() != null;
242     }
243 
isManagedProfile(Context context)244     public static boolean isManagedProfile(Context context) {
245         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
246         UserInfo user = um.getUserInfo(UserHandle.myUserId());
247         return user != null ? user.isManagedProfile() : false;
248     }
249 
250     /**
251      * Returns true if the given package does not exist on the device or if its version code is less
252      * than the given version, and false otherwise.
253      */
packageRequiresUpdate(String packageName, int minSupportedVersion, Context context)254     public static boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
255             Context context) {
256         try {
257             PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
258             if (packageInfo.versionCode >= minSupportedVersion) {
259                 return false;
260             }
261         } catch (NameNotFoundException e) {
262             // Package not on device.
263         }
264 
265         return true;
266     }
267 
stringToByteArray(String s)268     public static byte[] stringToByteArray(String s)
269         throws NumberFormatException {
270         try {
271             return Base64.decode(s, Base64.URL_SAFE);
272         } catch (IllegalArgumentException e) {
273             throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded.");
274         }
275     }
276 
byteArrayToString(byte[] bytes)277     public static String byteArrayToString(byte[] bytes) {
278         return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
279     }
280 
markDeviceProvisioned(Context context)281     public static void markDeviceProvisioned(Context context) {
282         if (isCurrentUserOwner()) {
283             // This only needs to be set once per device
284             Global.putInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
285         }
286 
287         // Setting this flag will either cause Setup Wizard to finish immediately when it starts (if
288         // it is not already running), or when its next activity starts (if it is already running,
289         // e.g. the non-NFC flow).
290         // When either of these things happen, a home intent is fired. We catch that in
291         // HomeReceiverActivity before sending the intent to notify the mdm that provisioning is
292         // complete.
293         // Note that, in the NFC flow or for secondary users, setting this flag will prevent the
294         // user from seeing SUW, even if no other device initialization app was specified.
295         Secure.putInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
296     }
297 
isUserSetupCompleted(Context context)298     public static boolean isUserSetupCompleted(Context context) {
299         return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0;
300     }
301 
getManagedProfile(Context context)302     public static UserHandle getManagedProfile(Context context) {
303         UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
304         int currentUserId = userManager.getUserHandle();
305         List<UserInfo> userProfiles = userManager.getProfiles(currentUserId);
306         for (UserInfo profile : userProfiles) {
307             if (profile.isManagedProfile()) {
308                 return new UserHandle(profile.id);
309             }
310         }
311         return null;
312     }
313 
314     /**
315      * @return The User id of an already existing managed profile or -1 if none
316      * exists
317      */
alreadyHasManagedProfile(Context context)318     public static int alreadyHasManagedProfile(Context context) {
319         UserHandle managedUser = getManagedProfile(context);
320         if (managedUser != null) {
321             return managedUser.getIdentifier();
322         } else {
323             return -1;
324         }
325     }
326 
removeAccount(Context context, Account account)327     public static void removeAccount(Context context, Account account) {
328         try {
329             AccountManager accountManager =
330                     (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
331             AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
332                     null, null /* callback */, null /* handler */);
333             // Block to get the result of the removeAccount operation
334             if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
335                 ProvisionLogger.logw("Account removed from the primary user.");
336             } else {
337                 Intent removeIntent = (Intent) bundle.getResult().getParcelable(
338                         AccountManager.KEY_INTENT);
339                 if (removeIntent != null) {
340                     ProvisionLogger.logi("Starting activity to remove account");
341                     TrampolineActivity.startActivity(context, removeIntent);
342                 } else {
343                     ProvisionLogger.logw("Could not remove account from the primary user.");
344                 }
345             }
346         } catch (OperationCanceledException | AuthenticatorException | IOException e) {
347             ProvisionLogger.logw("Exception removing account from the primary user.", e);
348         }
349     }
350 
isFrpSupported(Context context)351     public static boolean isFrpSupported(Context context) {
352         Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
353         return pdbManager != null;
354     }
355 
356 }
357