/* * Copyright 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.managedprovisioning.common; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC; import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED; import static java.nio.charset.StandardCharsets.UTF_8; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.graphics.Color; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.Base64; import java.io.IOException; import java.lang.Integer; import java.lang.Math; import java.lang.String; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import com.android.internal.annotations.VisibleForTesting; import com.android.managedprovisioning.FinalizationActivity; import com.android.managedprovisioning.ProvisionLogger; import com.android.managedprovisioning.TrampolineActivity; import com.android.managedprovisioning.model.ProvisioningParams; import com.android.managedprovisioning.model.PackageDownloadInfo; /** * Class containing various auxiliary methods. */ public class Utils { private static final int ACCOUNT_COPY_TIMEOUT_SECONDS = 60 * 3; // 3 minutes private static final int THRESHOLD_BRIGHT_COLOR = 160; // A color needs a brightness of at least // this value to be considered bright. (brightness being between 0 and 255). public Utils() {} /** * Returns the currently installed system apps on a given user. * *
Calls into the {@link IPackageManager} to retrieve all installed packages on the given
* user and returns the package names of all system apps.
*
* @param ipm an {@link IPackageManager} object
* @param userId the id of the user we are interested in
*/
public Set There are two cases where an update is required:
* 1. The package is not currently present on the device.
* 2. The package is present, but the version is below the minimum supported version.
*
* @param packageName the package to be checked for updates
* @param minSupportedVersion the minimum supported version
* @param context a {@link Context} object
*/
public boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
Context context) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
// Always download packages if no minimum version given.
if (minSupportedVersion != PackageDownloadInfo.DEFAULT_MINIMUM_VERSION
&& packageInfo.versionCode >= minSupportedVersion) {
return false;
}
} catch (NameNotFoundException e) {
// Package not on device.
}
return true;
}
/**
* Transforms a string into a byte array.
*
* @param s the string to be transformed
*/
public byte[] stringToByteArray(String s)
throws NumberFormatException {
try {
return Base64.decode(s, Base64.URL_SAFE);
} catch (IllegalArgumentException e) {
throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded.");
}
}
/**
* Transforms a byte array into a string.
*
* @param bytes the byte array to be transformed
*/
public String byteArrayToString(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
}
/**
* Sets user setup complete on a given user.
*
* This will set USER_SETUP_COMPLETE to 1 on the given user.
*/
public void markUserSetupComplete(Context context, int userId) {
ProvisionLogger.logd("Setting USER_SETUP_COMPLETE to 1 for user " + userId);
Secure.putIntForUser(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, userId);
}
/**
* Returns whether USER_SETUP_COMPLETE is set on the calling user.
*/
public boolean isUserSetupCompleted(Context context) {
return Secure.getInt(context.getContentResolver(), Secure.USER_SETUP_COMPLETE, 0) != 0;
}
/**
* Returns whether DEVICE_PROVISIONED is set.
*/
public boolean isDeviceProvisioned(Context context) {
return Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;
}
/**
* Set the current users userProvisioningState depending on the following factors:
* Note that we currently only support one managed profile per device.
*/
// TODO: Add unit tests
public UserHandle getManagedProfile(Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
int currentUserId = userManager.getUserHandle();
List This removes the given account from the calling user's list of accounts.
*
* @param context a {@link Context} object
* @param account the account to be removed
*/
// TODO: Add unit tests
public void removeAccount(Context context, Account account) {
try {
AccountManager accountManager =
(AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
AccountManagerFuture Copies a given account form {@code sourceUser} to {@code targetUser}. This call is
* blocking until the operation has succeeded. If within a timeout the account hasn't been
* successfully copied to the new user, we give up.
*
* @param context a {@link Context} object
* @param accountToMigrate the account to be migrated
* @param sourceUser the {@link UserHandle} of the user to copy from
* @param targetUser the {@link UserHandle} of the user to copy to
* @return whether account migration successfully completed
*/
public boolean maybeCopyAccount(Context context, Account accountToMigrate,
UserHandle sourceUser, UserHandle targetUser) {
if (accountToMigrate == null) {
ProvisionLogger.logd("No account to migrate.");
return false;
}
if (sourceUser.equals(targetUser)) {
ProvisionLogger.loge("sourceUser and targetUser are the same, won't migrate account.");
return false;
}
ProvisionLogger.logd("Attempting to copy account from " + sourceUser + " to " + targetUser);
try {
AccountManager accountManager =
(AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
boolean copySucceeded = accountManager.copyAccountToUser(
accountToMigrate,
sourceUser,
targetUser,
/* callback= */ null, /* handler= */ null)
.getResult(ACCOUNT_COPY_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (copySucceeded) {
ProvisionLogger.logi("Copied account to " + targetUser);
return true;
} else {
ProvisionLogger.loge("Could not copy account to " + targetUser);
}
} catch (OperationCanceledException | AuthenticatorException | IOException e) {
ProvisionLogger.loge("Exception copying account to " + targetUser, e);
}
return false;
}
/**
* Returns whether FRP is supported on the device.
*/
public boolean isFrpSupported(Context context) {
Object pdbManager = context.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
return pdbManager != null;
}
/**
* Translates a given managed provisioning intent to its corresponding provisioning flow, using
* the action from the intent.
*
* Encryption is required if the device is not currently encrypted and the persistent
* system flag {@code persist.sys.no_req_encrypt} is not set.
*/
public boolean isEncryptionRequired() {
return !isPhysicalDeviceEncrypted()
&& !SystemProperties.getBoolean("persist.sys.no_req_encrypt", false);
}
/**
* Returns whether the device is currently encrypted.
*/
public boolean isPhysicalDeviceEncrypted() {
return StorageManager.isEncrypted();
}
/**
* Returns the wifi pick intent.
*/
// TODO: Move this intent into a Globals class.
public Intent getWifiPickIntent() {
Intent wifiIntent = new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK);
wifiIntent.putExtra("extra_prefs_show_button_bar", true);
wifiIntent.putExtra("wifi_enable_next_on_connect", true);
return wifiIntent;
}
/**
* Returns whether the device has a split system user.
*
* Split system user means that user 0 is system only and all meat users are separate from
* the system user.
*/
public boolean isSplitSystemUser() {
return UserManager.isSplitSystemUser();
}
/**
* Returns whether the currently chosen launcher supports managed profiles.
*
* A launcher is deemed to support managed profiles when its target API version is at least
* {@link Build.VERSION_CODES#LOLLIPOP}.
*/
public boolean currentLauncherSupportsManagedProfiles(Context context) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
PackageManager pm = context.getPackageManager();
ResolveInfo launcherResolveInfo = pm.resolveActivity(intent,
PackageManager.MATCH_DEFAULT_ONLY);
if (launcherResolveInfo == null) {
return false;
}
try {
// If the user has not chosen a default launcher, then launcherResolveInfo will be
// referring to the resolver activity. It is fine to create a managed profile in
// this case since there will always be at least one launcher on the device that
// supports managed profile feature.
ApplicationInfo launcherAppInfo = pm.getApplicationInfo(
launcherResolveInfo.activityInfo.packageName, 0 /* default flags */);
return versionNumberAtLeastL(launcherAppInfo.targetSdkVersion);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
/**
* Returns whether the given version number is at least lollipop.
*
* @param versionNumber the version number to be verified.
*/
private boolean versionNumberAtLeastL(int versionNumber) {
return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
}
}
*
*
* @param context a {@link Context} object
* @param params configuration for current provisioning attempt
*/
// TODO: Add unit tests
public void markUserProvisioningStateInitiallyDone(Context context,
ProvisioningParams params) {
int currentUserId = UserHandle.myUserId();
int managedProfileUserId = UserHandle.USER_NULL;
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
// new provisioning state for current user, if non-null
Integer newState = null;
// New provisioning state for managed-profile of current user, if non-null.
Integer newProfileState = null;
boolean userSetupCompleted = isUserSetupCompleted(context);
if (params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
// Managed profiles are a special case as two users are involved.
managedProfileUserId = getManagedProfile(context).getIdentifier();
if (userSetupCompleted) {
// SUW on current user is complete, so nothing much to do beyond indicating we're
// all done.
newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
} else {
// We're still in SUW, so indicate that a managed-profile was setup on current user,
// and that we're awaiting finalization on both.
newState = DevicePolicyManager.STATE_USER_PROFILE_COMPLETE;
newProfileState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE;
}
} else if (userSetupCompleted) {
// User setup was previously completed this is an unexpected case.
ProvisionLogger.logw("user_setup_complete set, but provisioning was started");
} else if (params.skipUserSetup) {
// DPC requested setup-wizard is skipped, indicate this to SUW.
newState = DevicePolicyManager.STATE_USER_SETUP_COMPLETE;
} else {
// DPC requested setup-wizard is not skipped, indicate this to SUW.
newState = DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE;
}
if (newState != null) {
setUserProvisioningState(dpm, newState, currentUserId);
}
if (newProfileState != null) {
setUserProvisioningState(dpm, newProfileState, managedProfileUserId);
}
if (!userSetupCompleted) {
// We expect a PROVISIONING_FINALIZATION intent to finish setup if we're still in
// user-setup.
FinalizationActivity.storeProvisioningParams(context, params);
}
}
/**
* Finalize the current users userProvisioningState depending on the following factors:
*
*
*
* @param context a {@link Context} object
* @param params configuration for current provisioning attempt - if null (because
* ManagedProvisioning wasn't used for first phase of provisioning) aassumes we
* can just mark current user as being in finalized provisioning state
*/
// TODO: Add unit tests
public void markUserProvisioningStateFinalized(Context context,
ProvisioningParams params) {
int currentUserId = UserHandle.myUserId();
int managedProfileUserId = -1;
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
Integer newState = null;
Integer newProfileState = null;
if (params != null && params.provisioningAction.equals(ACTION_PROVISION_MANAGED_PROFILE)) {
// Managed profiles are a special case as two users are involved.
managedProfileUserId = getManagedProfile(context).getIdentifier();
newState = DevicePolicyManager.STATE_USER_UNMANAGED;
newProfileState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
} else {
newState = DevicePolicyManager.STATE_USER_SETUP_FINALIZED;
}
if (newState != null) {
setUserProvisioningState(dpm, newState, currentUserId);
}
if (newProfileState != null) {
setUserProvisioningState(dpm, newProfileState, managedProfileUserId);
}
}
private void setUserProvisioningState(DevicePolicyManager dpm, int state, int userId) {
ProvisionLogger.logi("Setting userProvisioningState for user " + userId + " to: " + state);
dpm.setUserProvisioningState(state, userId);
}
/**
* Returns the first existing managed profile if any present, null otherwise.
*
*