/* * 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 android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.annotation.NonNull; import android.annotation.Nullable; 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.os.Build; import android.os.Bundle; 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.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.managedprovisioning.TrampolineActivity; import com.android.managedprovisioning.model.PackageDownloadInfo; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Class containing various auxiliary methods. */ public class Utils { public static final String SHA256_TYPE = "SHA-256"; public static final String SHA1_TYPE = "SHA-1"; // value chosen to match UX designs; when updating check status bar icon colors private static final int THRESHOLD_BRIGHT_COLOR = 190; 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 This function returns {@code null} if no or multiple admin receivers were found, and if
* the package name does not match dpcPackageName. 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;
}
/**
* Returns the first existing managed profile if any present, null otherwise.
*
* 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 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;
}
/**
* Computes the sha 256 hash of a byte array.
*/
@Nullable
public byte[] computeHashOfByteArray(byte[] bytes) {
try {
MessageDigest md = MessageDigest.getInstance(SHA256_TYPE);
md.update(bytes);
return md.digest();
} catch (NoSuchAlgorithmException e) {
ProvisionLogger.loge("Hashing algorithm " + SHA256_TYPE + " not supported.", e);
return null;
}
}
/**
* Computes a hash of a file with a spcific hash algorithm.
*/
// TODO: Add unit tests
@Nullable
public byte[] computeHashOfFile(String fileLocation, String hashType) {
InputStream fis = null;
MessageDigest md;
byte hash[] = null;
try {
md = MessageDigest.getInstance(hashType);
} catch (NoSuchAlgorithmException e) {
ProvisionLogger.loge("Hashing algorithm " + hashType + " not supported.", e);
return null;
}
try {
fis = new FileInputStream(fileLocation);
byte[] buffer = new byte[256];
int n = 0;
while (n != -1) {
n = fis.read(buffer);
if (n > 0) {
md.update(buffer, 0, n);
}
}
hash = md.digest();
} catch (IOException e) {
ProvisionLogger.loge("IO error.", e);
} finally {
// Close input stream quietly.
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
// Ignore.
}
}
return hash;
}
public boolean isBrightColor(int color) {
// This comes from the YIQ transformation. We're using the formula:
// Y = .299 * R + .587 * G + .114 * B
return Color.red(color) * 299 + Color.green(color) * 587 + Color.blue(color) * 114
>= 1000 * THRESHOLD_BRIGHT_COLOR;
}
/**
* Returns whether given intent can be resolved for the user.
*/
public boolean canResolveIntentAsUser(Context context, Intent intent, int userId) {
return intent != null
&& context.getPackageManager().resolveActivityAsUser(intent, 0, userId) != null;
}
public boolean isPackageDeviceOwner(DevicePolicyManager dpm, String packageName) {
final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnCallingUser();
return deviceOwner != null && deviceOwner.getPackageName().equals(packageName);
}
}