package com.android.server.devicepolicy;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
+import static android.app.admin.DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.NetworkEvent;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.SystemUpdatePolicy;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
+import android.net.IIpConnectivityMetrics;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
+import android.security.KeyStore;
import android.service.persistentdata.PersistentDataBlockManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
private static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
private static final int PROFILE_WIPED_NOTIFICATION_ID = 1001;
+ private static final int NETWORK_LOGGING_NOTIFICATION_ID = 1002;
private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
private static final String ATTR_SETUP_COMPLETE = "setup-complete";
private static final String ATTR_PERMISSION_POLICY = "permission-policy";
private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
"device-provisioning-config-applied";
+ private static final String ATTR_DEVICE_PAIRED = "device-paired";
private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
private static final int CODE_NONSYSTEM_USER_EXISTS = 5;
private static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
private static final int CODE_NOT_SYSTEM_USER = 7;
+ private static final int CODE_HAS_PAIRED = 8;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
+ /**
+ * Minimum timeout in milliseconds after which unlocking with weak auth times out,
+ * i.e. the user has to use a strong authentication method like password, PIN or pattern.
+ */
+ private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
*/
boolean mHasFeature;
+ /**
+ * Whether or not this device is a watch.
+ */
+ boolean mIsWatch;
+
private final SecurityLogMonitor mSecurityLogMonitor;
+ private NetworkLogger mNetworkLogger;
private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
int mPasswordOwner = -1;
long mLastMaximumTimeToLock = -1;
boolean mUserSetupComplete = false;
+ boolean mPaired = false;
int mUserProvisioningState;
int mPermissionPolicy;
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
getSendingUserId());
+ /*
+ * Network logging would ideally be started in setDeviceOwnerSystemPropertyLocked(),
+ * however it's too early in the boot process to register with IIpConnectivityMetrics
+ * to listen for events.
+ */
+ if (Intent.ACTION_USER_STARTED.equals(action)
+ && userHandle == mOwners.getDeviceOwnerUserId()) {
+ synchronized (DevicePolicyManagerService.this) {
+ if (isNetworkLoggingEnabledInternalLocked()) {
+ setNetworkLoggingActiveInternal(true);
+ }
+ }
+ }
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
&& userHandle == mOwners.getDeviceOwnerUserId()
&& getDeviceOwnerRemoteBugreportUri() != null) {
new MonitoringCertNotificationTask().execute(userId);
}
if (Intent.ACTION_USER_ADDED.equals(action)) {
- disableSecurityLoggingIfNotCompliant();
+ disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- disableSecurityLoggingIfNotCompliant();
+ disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
removeUserData(userHandle);
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
synchronized (DevicePolicyManagerService.this) {
static class ActiveAdmin {
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
+ private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
+ private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
private static final String TAG_ACCOUNT_TYPE = "account-type";
private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES
= "permitted-accessiblity-services";
private static final String TAG_PERMITTED_IMES = "permitted-imes";
private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max-failed-password-wipe";
private static final String TAG_MAX_TIME_TO_UNLOCK = "max-time-to-unlock";
+ private static final String TAG_STRONG_AUTH_UNLOCK_TIMEOUT = "strong-auth-unlock-timeout";
private static final String TAG_MIN_PASSWORD_NONLETTER = "min-password-nonletter";
private static final String TAG_MIN_PASSWORD_SYMBOLS = "min-password-symbols";
private static final String TAG_MIN_PASSWORD_NUMERIC = "min-password-numeric";
private static final String TAG_PARENT_ADMIN = "parent-admin";
private static final String TAG_ORGANIZATION_COLOR = "organization-color";
private static final String TAG_ORGANIZATION_NAME = "organization-name";
+ private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
+ private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
final DeviceAdminInfo info;
static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
+ long strongAuthUnlockTimeout = 0; // admin doesn't participate by default
+
static final int DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE = 0;
int maximumFailedPasswordsForWipe = DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE;
int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
boolean encryptionRequested = false;
+ boolean testOnlyAdmin = false;
boolean disableCamera = false;
boolean disableCallerId = false;
boolean disableContactsSearch = false;
boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
boolean requireAutoTime = false; // Can only be set by a device owner.
boolean forceEphemeralUsers = false; // Can only be set by a device owner.
+ boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
+
+ // one notification after enabling + 3 more after reboots
+ static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 4;
+ int numNetworkLoggingNotifications = 0;
+ long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
ActiveAdmin parentAdmin;
final boolean isParent;
out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock));
out.endTag(null, TAG_MAX_TIME_TO_UNLOCK);
}
+ if (strongAuthUnlockTimeout != DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
+ out.startTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
+ out.attribute(null, ATTR_VALUE, Long.toString(strongAuthUnlockTimeout));
+ out.endTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
+ }
if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
out.startTag(null, TAG_MAX_FAILED_PASSWORD_WIPE);
out.attribute(null, ATTR_VALUE, Integer.toString(maximumFailedPasswordsForWipe));
out.attribute(null, ATTR_VALUE, Boolean.toString(encryptionRequested));
out.endTag(null, TAG_ENCRYPTION_REQUESTED);
}
+ if (testOnlyAdmin) {
+ out.startTag(null, TAG_TEST_ONLY_ADMIN);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(testOnlyAdmin));
+ out.endTag(null, TAG_TEST_ONLY_ADMIN);
+ }
if (disableCamera) {
out.startTag(null, TAG_DISABLE_CAMERA);
out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers));
out.endTag(null, TAG_FORCE_EPHEMERAL_USERS);
}
+ if (isNetworkLoggingEnabled) {
+ out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled));
+ out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS,
+ Integer.toString(numNetworkLoggingNotifications));
+ out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION,
+ Long.toString(lastNetworkLoggingNotificationTimeMs));
+ out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
+ }
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
} else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
+ strongAuthUnlockTimeout = Long.parseLong(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) {
maximumFailedPasswordsForWipe = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) {
encryptionRequested = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_TEST_ONLY_ADMIN.equals(tag)) {
+ testOnlyAdmin = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_DISABLE_CAMERA.equals(tag)) {
disableCamera = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) {
forceEphemeralUsers = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) {
+ isNetworkLoggingEnabled = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ lastNetworkLoggingNotificationTimeMs = Long.parseLong(
+ parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION));
+ numNetworkLoggingNotifications = Integer.parseInt(
+ parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS));
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
disabledKeyguardFeatures = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("uid="); pw.println(getUid());
+ pw.print(prefix); pw.print("testOnlyAdmin=");
+ pw.println(testOnlyAdmin);
pw.print(prefix); pw.println("policies:");
ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
if (pols != null) {
pw.println(minimumPasswordNonLetter);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
+ pw.print(prefix); pw.print("strongAuthUnlockTimeout=");
+ pw.println(strongAuthUnlockTimeout);
pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
pw.println(maximumFailedPasswordsForWipe);
pw.print(prefix); pw.print("specifiesGlobalProxy=");
pw.println(requireAutoTime);
pw.print(prefix); pw.print("forceEphemeralUsers=");
pw.println(forceEphemeralUsers);
+ pw.print(prefix); pw.print("isNetworkLoggingEnabled=");
+ pw.println(isNetworkLoggingEnabled);
pw.print(prefix); pw.print("disabledKeyguardFeatures=");
pw.println(disabledKeyguardFeatures);
pw.print(prefix); pw.print("crossProfileWidgetProviders=");
return mContext.getSystemService(NotificationManager.class);
}
+ IIpConnectivityMetrics getIIpConnectivityMetrics() {
+ return (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ }
+
PowerManagerInternal getPowerManagerInternal() {
return LocalServices.getService(PowerManagerInternal.class);
}
return (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
}
+ AlarmManager getAlarmManager() {
+ return (AlarmManager) mContext.getSystemService(AlarmManager.class);
+ }
+
IWindowManager getIWindowManager() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
mHasFeature = mContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
+ mIsWatch = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_WATCH);
if (!mHasFeature) {
// Skip the rest of the initialization
return;
}
private void setDeviceOwnerSystemPropertyLocked() {
- // Device owner may still be provisioned, do not set the read-only system property yet.
- if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+ final boolean deviceProvisioned =
+ mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ // If the device is not provisioned and there is currently no device owner, do not set the
+ // read-only system property yet, since Device owner may still be provisioned. For Wear
+ // devices, if there is already a device owner then it's OK to set the property to true now,
+ // regardless the provision state.
+ final boolean isWatchWithDeviceOwner = mIsWatch && mOwners.hasDeviceOwner();
+ if (!isWatchWithDeviceOwner && !deviceProvisioned) {
return;
}
// Still at the first stage of CryptKeeper double bounce, mOwners.hasDeviceOwner is
if (mOwners.hasDeviceOwner()) {
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "true");
Slog.i(LOG_TAG, "Set ro.device_owner property to true");
- disableSecurityLoggingIfNotCompliant();
+ disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
if (mInjector.securityLogGetLoggingEnabledProperty()) {
mSecurityLogMonitor.start();
}
long token = mInjector.binderClearCallingIdentity();
try {
int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ AlarmManager am = mInjector.getAlarmManager();
PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT,
out.attribute(null, ATTR_SETUP_COMPLETE,
Boolean.toString(true));
}
+ if (policy.mPaired) {
+ out.attribute(null, ATTR_DEVICE_PAIRED,
+ Boolean.toString(true));
+ }
if (policy.mDeviceProvisioningConfigApplied) {
out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED,
Boolean.toString(true));
if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
policy.mUserSetupComplete = true;
}
+ String paired = parser.getAttributeValue(null, ATTR_DEVICE_PAIRED);
+ if (paired != null && Boolean.toString(true).equals(paired)) {
+ policy.mPaired = true;
+ }
String deviceProvisioningConfigApplied = parser.getAttributeValue(null,
ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED);
if (deviceProvisioningConfigApplied != null
// Register an observer for watching for user setup complete.
new SetupContentObserver(mHandler).register();
// Initialize the user setup state, to handle the upgrade case.
- updateUserSetupComplete();
+ updateUserSetupCompleteAndPaired();
List<String> packageList;
synchronized (this) {
synchronized (this) {
long ident = mInjector.binderClearCallingIdentity();
try {
- if (!refreshing
- && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
+ final ActiveAdmin existingAdmin
+ = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (!refreshing && existingAdmin != null) {
throw new IllegalArgumentException("Admin is already added");
}
if (policy.mRemovingAdmins.contains(adminReceiver)) {
"Trying to set an admin which is being removed");
}
ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
+ newAdmin.testOnlyAdmin =
+ (existingAdmin != null) ? existingAdmin.testOnlyAdmin
+ : isPackageTestOnly(adminReceiver.getPackageName(), userHandle);
policy.mAdminMap.put(adminReceiver, newAdmin);
int replaceIndex = -1;
final int N = policy.mAdminList.size();
enforceShell("forceRemoveActiveAdmin");
long ident = mInjector.binderClearCallingIdentity();
try {
- final ApplicationInfo ai;
- try {
- ai = mIPackageManager.getApplicationInfo(adminReceiver.getPackageName(),
- 0, userHandle);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- if (ai == null) {
- throw new IllegalStateException("Couldn't find package to remove admin "
- + adminReceiver.getPackageName() + " " + userHandle);
- }
- if ((ai.flags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
- throw new SecurityException("Attempt to remove non-test admin " + adminReceiver
- + adminReceiver + " " + userHandle);
- }
- // If admin is a device or profile owner tidy that up first.
synchronized (this) {
+ if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
+ throw new SecurityException("Attempt to remove non-test admin "
+ + adminReceiver + " " + userHandle);
+ }
+
+ // If admin is a device or profile owner tidy that up first.
if (isDeviceOwner(adminReceiver, userHandle)) {
clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
}
}
// Remove the admin skipping sending the broadcast.
removeAdminArtifacts(adminReceiver, userHandle);
+ Slog.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
+ /**
+ * Return if a given package has testOnly="true", in which case we'll relax certain rules
+ * for CTS.
+ *
+ * DO NOT use this method except in {@link #setActiveAdmin}. Use {@link #isAdminTestOnlyLocked}
+ * to check wehter an active admin is test-only or not.
+ *
+ * The system allows this flag to be changed when an app is updated, which is not good
+ * for us. So we persist the flag in {@link ActiveAdmin} when an admin is first installed,
+ * and used the persisted version in actual checks. (See b/31382361 and b/28928996)
+ */
+ private boolean isPackageTestOnly(String packageName, int userHandle) {
+ final ApplicationInfo ai;
+ try {
+ ai = mIPackageManager.getApplicationInfo(packageName,
+ (PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE), userHandle);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ if (ai == null) {
+ throw new IllegalStateException("Couldn't find package: "
+ + packageName + " on user " + userHandle);
+ }
+ return (ai.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0;
+ }
+
+ /**
+ * See {@link #isPackageTestOnly}.
+ */
+ private boolean isAdminTestOnlyLocked(ComponentName who, int userHandle) {
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return (admin != null) && admin.testOnlyAdmin;
+ }
+
private void enforceShell(String method) {
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
@Override
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+ if (!isCallerWithSystemUid()) {
+ throw new SecurityException("Caller must be system");
+ }
ComponentName profileOwner = getProfileOwner(userHandle);
// Profile challenge is supported on N or newer release.
return profileOwner != null &&
DevicePolicyData policy, int userHandle, boolean parent) {
enforceUserUnlocked(userHandle, parent);
- if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle, parent)
- || policy.mActivePasswordLength < getPasswordMinimumLength(
+ final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
+ if (policy.mActivePasswordQuality < requiredPasswordQuality) {
+ return false;
+ }
+ if (requiredPasswordQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ && policy.mActivePasswordLength < getPasswordMinimumLength(
null, userHandle, parent)) {
return false;
}
- if (policy.mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+ if (requiredPasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
return true;
}
return policy.mActivePasswordUpperCase >= getPasswordMinimumUpperCase(
}
@Override
+ public void setRequiredStrongAuthTimeout(ComponentName who, long timeoutMs,
+ boolean parent) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
+ // timeoutMs with value 0 means that the admin doesn't participate
+ // timeoutMs is clamped to the interval in case the internal constants change in the future
+ if (timeoutMs != 0 && timeoutMs < MINIMUM_STRONG_AUTH_TIMEOUT_MS) {
+ timeoutMs = MINIMUM_STRONG_AUTH_TIMEOUT_MS;
+ }
+ if (timeoutMs > DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
+ timeoutMs = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+ }
+
+ final int userHandle = mInjector.userHandleGetCallingUserId();
+ synchronized (this) {
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+ if (ap.strongAuthUnlockTimeout != timeoutMs) {
+ ap.strongAuthUnlockTimeout = timeoutMs;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ /**
+ * Return a single admin's strong auth unlock timeout or minimum value (strictest) of all
+ * admins if who is null.
+ * Returns 0 if not configured for the provided admin.
+ */
+ @Override
+ public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
+ if (!mHasFeature) {
+ return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+ }
+ enforceFullCrossUsersPermission(userId);
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId, parent);
+ return admin != null ? admin.strongAuthUnlockTimeout : 0;
+ }
+
+ // Return the strictest policy across all participating admins.
+ List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId, parent);
+
+ long strongAuthUnlockTimeout = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
+ for (int i = 0; i < admins.size(); i++) {
+ final long timeout = admins.get(i).strongAuthUnlockTimeout;
+ if (timeout != 0) { // take only participating admins into account
+ strongAuthUnlockTimeout = Math.min(timeout, strongAuthUnlockTimeout);
+ }
+ }
+ return Math.max(strongAuthUnlockTimeout, MINIMUM_STRONG_AUTH_TIMEOUT_MS);
+ }
+ }
+
+ @Override
public void lockNow(boolean parent) {
if (!mHasFeature) {
return;
* not installed and therefore not available.
*
* @throws SecurityException if the caller is not a profile or device owner.
- * @throws UnsupportedException if the package does not support being set as always-on.
+ * @throws UnsupportedOperationException if the package does not support being set as always-on.
*/
@Override
public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown)
}
boolean legacyApp = false;
- if (ai.targetSdkVersion <= Build.VERSION_CODES.M) {
- legacyApp = true;
+ if (ai != null) {
+ if (ai.targetSdkVersion <= Build.VERSION_CODES.M) {
+ legacyApp = true;
+ }
}
final int rawStatus = getEncryptionStatus();
intent.setComponent(mOwners.getDeviceOwnerComponent());
intent.setDataAndType(bugreportUri, RemoteBugreportUtils.BUGREPORT_MIMETYPE);
intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
- mContext.grantUriPermission(mOwners.getDeviceOwnerComponent().getPackageName(),
- bugreportUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ LocalServices.getService(ActivityManagerInternal.class)
+ .grantUriPermissionFromIntent(Process.SHELL_UID,
+ mOwners.getDeviceOwnerComponent().getPackageName(),
+ intent, mOwners.getDeviceOwnerUserId());
mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
}
} catch (FileNotFoundException e) {
}
/**
+ * @hide
+ */
+ @Override
+ public boolean requireSecureKeyguard(int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ int passwordQuality = getPasswordQuality(null, userHandle, false);
+ if (passwordQuality > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ return true;
+ }
+
+ int encryptionStatus = getStorageEncryptionStatus(null, userHandle);
+ if (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ return true;
+ }
+
+ final int keyguardDisabledFeatures = getKeyguardDisabledFeatures(null, userHandle, false);
+ return (keyguardDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+ }
+
+
+ /**
* Gets the disabled state for features in keyguard for the given admin,
* or the aggregate of all active admins if who is null.
*/
throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
+ final boolean hasIncompatibleAccountsOrNonAdb =
+ hasIncompatibleAccountsOrNonAdbNoLock(userId, admin);
synchronized (this) {
- enforceCanSetDeviceOwnerLocked(userId);
+ enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb);
if (getActiveAdminUncheckedLocked(admin, userId) == null
|| getUserData(userId).mRemovingAdmins.contains(admin)) {
throw new IllegalArgumentException("Not active admin: " + admin);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
+ Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
return true;
}
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
+ Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent);
}
}
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
- disableSecurityLoggingIfNotCompliant();
+ disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
try {
- // Reactivate backup service.
- mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ if (mInjector.getIBackupManager() != null) {
+ // Reactivate backup service.
+ mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ }
} catch (RemoteException e) {
throw new IllegalStateException("Failed reactivating backup service.", e);
}
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
+ final boolean hasIncompatibleAccountsOrNonAdb =
+ hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who);
synchronized (this) {
- enforceCanSetProfileOwnerLocked(userHandle);
+ enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb);
if (getActiveAdminUncheckedLocked(who, userHandle) == null
|| getUserData(userHandle).mRemovingAdmins.contains(who)) {
mOwners.setProfileOwner(who, ownerName, userHandle);
mOwners.writeProfileOwner(userHandle);
+ Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
return true;
}
}
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
+ Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId);
}
}
return hasUserSetupCompleted(UserHandle.getCallingUserId());
}
+ // This checks only if the Setup Wizard has run. Since Wear devices pair before
+ // completing Setup Wizard, and pairing involves transferring user data, calling
+ // logic may want to check mIsWatch or mPaired in addition to hasUserSetupCompleted().
private boolean hasUserSetupCompleted(int userHandle) {
if (!mHasFeature) {
return true;
return getUserData(userHandle).mUserSetupComplete;
}
+ private boolean hasPaired(int userHandle) {
+ if (!mHasFeature) {
+ return true;
+ }
+ return getUserData(userHandle).mPaired;
+ }
+
@Override
public int getUserProvisioningState() {
if (!mHasFeature) {
* The profile owner can only be set before the user setup phase has completed,
* except for:
* - SYSTEM_UID
- * - adb if there are not accounts.
+ * - adb unless hasIncompatibleAccountsOrNonAdb is true.
*/
- private void enforceCanSetProfileOwnerLocked(int userHandle) {
+ private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
+ boolean hasIncompatibleAccountsOrNonAdb) {
UserInfo info = getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
}
int callingUid = mInjector.binderGetCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
- if (hasUserSetupCompleted(userHandle) &&
- AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
+ if ((mIsWatch || hasUserSetupCompleted(userHandle))
+ && hasIncompatibleAccountsOrNonAdb) {
throw new IllegalStateException("Not allowed to set the profile owner because "
+ "there are already some accounts on the profile");
}
return;
}
enforceCanManageProfileAndDeviceOwners();
- if (hasUserSetupCompleted(userHandle) && !isCallerWithSystemUid()) {
+ if ((mIsWatch || hasUserSetupCompleted(userHandle)) && !isCallerWithSystemUid()) {
throw new IllegalStateException("Cannot set the profile owner on a user which is "
+ "already set-up");
}
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
*/
- private void enforceCanSetDeviceOwnerLocked(int userId) {
+ private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId,
+ boolean hasIncompatibleAccountsOrNonAdb) {
int callingUid = mInjector.binderGetCallingUid();
boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
if (!isAdb) {
enforceCanManageProfileAndDeviceOwners();
}
- final int code = checkSetDeviceOwnerPreCondition(userId, isAdb);
+ final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb,
+ hasIncompatibleAccountsOrNonAdb);
switch (code) {
case CODE_OK:
return;
case CODE_ACCOUNTS_NOT_EMPTY:
throw new IllegalStateException("Not allowed to set the device owner because there "
+ "are already some accounts on the device");
+ case CODE_HAS_PAIRED:
+ throw new IllegalStateException("Not allowed to set the device owner because this "
+ + "device has already paired");
default:
throw new IllegalStateException("Unknown @DeviceOwnerPreConditionCode " + code);
}
}
}
+ private void enforceDeviceOwnerOrManageUsers() {
+ synchronized (this) {
+ if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ mInjector.binderGetCallingUid()) != null) {
+ return;
+ }
+ }
+ enforceManageUsers();
+ }
+
private void ensureCallerPackage(@Nullable String packageName) {
if (packageName == null) {
Preconditions.checkState(isCallerWithSystemUid(),
if (!mHasFeature) {
return null;
}
+ enforceManageUsers();
synchronized (this) {
List<String> result = null;
// If we have multiple profiles we return the intersection of the
}
/**
- * We need to update the internal state of whether a user has completed setup once. After
- * that, we ignore any changes that reset the Settings.Secure.USER_SETUP_COMPLETE changes
- * as we don't trust any apps that might try to reset it.
+ * We need to update the internal state of whether a user has completed setup or a
+ * device has paired once. After that, we ignore any changes that reset the
+ * Settings.Secure.USER_SETUP_COMPLETE or Settings.Secure.DEVICE_PAIRED change
+ * as we don't trust any apps that might try to reset them.
* <p>
* Unfortunately, we don't know which user's setup state was changed, so we write all of
* them.
*/
- void updateUserSetupComplete() {
+ void updateUserSetupCompleteAndPaired() {
List<UserInfo> users = mUserManager.getUsers(true);
final int N = users.size();
for (int i = 0; i < N; i++) {
}
}
}
+ if (mIsWatch && mInjector.settingsSecureGetIntForUser(Settings.Secure.DEVICE_PAIRED, 0,
+ userHandle) != 0) {
+ DevicePolicyData policy = getUserData(userHandle);
+ if (!policy.mPaired) {
+ policy.mPaired = true;
+ synchronized (this) {
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
}
}
Settings.Secure.USER_SETUP_COMPLETE);
private final Uri mDeviceProvisioned = Settings.Global.getUriFor(
Settings.Global.DEVICE_PROVISIONED);
+ private final Uri mPaired = Settings.Secure.getUriFor(Settings.Secure.DEVICE_PAIRED);
public SetupContentObserver(Handler handler) {
super(handler);
void register() {
mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
+ if (mIsWatch) {
+ mInjector.registerContentObserver(mPaired, false, this, UserHandle.USER_ALL);
+ }
}
@Override
public void onChange(boolean selfChange, Uri uri) {
- if (mUserSetupComplete.equals(uri)) {
- updateUserSetupComplete();
+ if (mUserSetupComplete.equals(uri) || (mIsWatch && mPaired.equals(uri))) {
+ updateUserSetupCompleteAndPaired();
} else if (mDeviceProvisioned.equals(uri)) {
synchronized (DevicePolicyManagerService.this) {
// Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property
< android.os.Build.VERSION_CODES.M) {
return false;
}
+ if (!isRuntimePermission(permission)) {
+ EventLog.writeEvent(0x534e4554, "62623498", user.getIdentifier(), "");
+ return false;
+ }
final PackageManager packageManager = mContext.getPackageManager();
switch (grantState) {
case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
return true;
} catch (SecurityException se) {
return false;
+ } catch (NameNotFoundException e) {
+ return false;
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
}
+ public boolean isRuntimePermission(String permissionName) throws NameNotFoundException {
+ final PackageManager packageManager = mContext.getPackageManager();
+ PermissionInfo permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+ return (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
+
@Override
public boolean isProvisioningAllowed(String action) {
if (!mHasFeature) {
if (hasUserSetupCompleted(callingUserId)) {
return false;
}
+ if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
+ return false;
+ }
return true;
} else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE.equals(action)) {
if (!mInjector.userManagerIsSplitSystemUser()) {
* The device owner can only be set before the setup phase of the primary user has completed,
* except for adb command if no accounts or additional users are present on the device.
*/
- private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition(
- int deviceOwnerUserId, boolean isAdb) {
+ private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreConditionLocked(
+ @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb,
+ boolean hasIncompatibleAccountsOrNonAdb) {
if (mOwners.hasDeviceOwner()) {
return CODE_HAS_DEVICE_OWNER;
}
if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
return CODE_USER_NOT_RUNNING;
}
+ if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
+ return CODE_HAS_PAIRED;
+ }
if (isAdb) {
// if shell command runs after user setup completed check device status. Otherwise, OK.
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
if (!mInjector.userManagerIsSplitSystemUser()) {
if (mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
- if (AccountManager.get(mContext).getAccounts().length > 0) {
+ if (hasIncompatibleAccountsOrNonAdb) {
return CODE_ACCOUNTS_NOT_EMPTY;
}
} else {
}
private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
- return CODE_OK == checkSetDeviceOwnerPreCondition(deviceOwnerUserId, /* isAdb */ false);
+ synchronized (this) {
+ return CODE_OK == checkSetDeviceOwnerPreConditionLocked(
+ /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false,
+ /* hasIncompatibleAccountsOrNonAdb=*/ true);
+ }
}
private boolean hasFeatureManagedUsers() {
return false;
}
- private synchronized void disableSecurityLoggingIfNotCompliant() {
- if (!isDeviceOwnerManagedSingleUserDevice()) {
+ private synchronized void disableDeviceOwnerManagedSingleUserFeaturesIfNeeded() {
+ final boolean isSingleUserManagedDevice = isDeviceOwnerManagedSingleUserDevice();
+
+ // disable security logging if needed
+ if (!isSingleUserManagedDevice) {
mInjector.securityLogSetLoggingEnabledProperty(false);
- Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user device.");
+ Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user managed"
+ + " device.");
+ }
+
+ // disable backup service if needed
+ // note: when clearing DO, the backup service shouldn't be disabled if it was enabled by
+ // the device owner
+ if (mOwners.hasDeviceOwner() && !isSingleUserManagedDevice) {
+ setBackupServiceEnabledInternal(false);
+ Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user.");
+ }
+
+ // disable network logging if needed
+ if (!isSingleUserManagedDevice) {
+ setNetworkLoggingActiveInternal(false);
+ Slog.w(LOG_TAG, "Network logging turned off as it's no longer a single user managed"
+ + " device.");
+ // if there still is a device owner, disable logging policy, otherwise the admin
+ // has been nuked
+ if (mOwners.hasDeviceOwner()) {
+ getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = false;
+ saveSettingsLocked(mOwners.getDeviceOwnerUserId());
+ }
}
}
saveSettingsLocked(userHandle);
updateMaximumTimeToLockLocked(userHandle);
policy.mRemovingAdmins.remove(adminReceiver);
+
+ Slog.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
}
// The removed admin might have disabled camera, so update user
// restrictions.
return policy.mDeviceProvisioningConfigApplied;
}
}
+
+ /**
+ * Return true if a given user has any accounts that'll prevent installing a device or profile
+ * owner {@code owner}.
+ * - If the user has no accounts, then return false.
+ * - Otherwise, if the owner is unknown (== null), or is not test-only, then return true.
+ * - Otherwise, if there's any account that does not have ..._ALLOWED, or does have
+ * ..._DISALLOWED, return true.
+ * - Otherwise return false.
+ *
+ * If the caller is *not* ADB, it also returns true. The returned value shouldn't be used
+ * when the caller is not ADB.
+ *
+ * DO NOT CALL IT WITH THE DPMS LOCK HELD.
+ */
+ private boolean hasIncompatibleAccountsOrNonAdbNoLock(
+ int userId, @Nullable ComponentName owner) {
+ final boolean isAdb = (mInjector.binderGetCallingUid() == Process.SHELL_UID)
+ || (mInjector.binderGetCallingUid() == Process.ROOT_UID);
+ if (!isAdb) {
+ return true;
+ }
+
+ if (Thread.holdsLock(this)) {
+ Slog.wtf(LOG_TAG, "hasIncompatibleAccountsNoLock() called with the DPMS lock held.");
+ return true;
+ }
+
+ final long token = mInjector.binderClearCallingIdentity();
+ try {
+ final AccountManager am = AccountManager.get(mContext);
+ final Account accounts[] = am.getAccountsAsUser(userId);
+ if (accounts.length == 0) {
+ return false;
+ }
+ synchronized (this) {
+ if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
+ Log.w(LOG_TAG,
+ "Non test-only owner can't be installed with existing accounts.");
+ return true;
+ }
+ }
+
+ final String[] feature_allow =
+ { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
+ final String[] feature_disallow =
+ { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
+
+ boolean compatible = true;
+ for (Account account : accounts) {
+ if (hasAccountFeatures(am, account, feature_disallow)) {
+ Log.e(LOG_TAG, account + " has " + feature_disallow[0]);
+ compatible = false;
+ break;
+ }
+ if (!hasAccountFeatures(am, account, feature_allow)) {
+ Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]);
+ compatible = false;
+ break;
+ }
+ }
+ if (compatible) {
+ Log.w(LOG_TAG, "All accounts are compatible");
+ } else {
+ Log.e(LOG_TAG, "Found incompatible accounts");
+ }
+ return !compatible;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ }
+
+ private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) {
+ try {
+ return am.hasFeatures(account, features, null, null).getResult();
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "Failed to get account feature", e);
+ return false;
+ }
+ }
+
+ @Override
+ public void setBackupServiceEnabled(ComponentName admin, boolean enabled) {
+ Preconditions.checkNotNull(admin);
+ if (!mHasFeature) {
+ return;
+ }
+ ensureDeviceOwnerManagingSingleUser(admin);
+ setBackupServiceEnabledInternal(enabled);
+ }
+
+ private synchronized void setBackupServiceEnabledInternal(boolean enabled) {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ IBackupManager ibm = mInjector.getIBackupManager();
+ if (ibm != null) {
+ ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed " + (enabled ? "" : "de") + "activating backup service.", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public boolean isBackupServiceEnabled(ComponentName admin) {
+ Preconditions.checkNotNull(admin);
+ if (!mHasFeature) {
+ return true;
+ }
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ try {
+ IBackupManager ibm = mInjector.getIBackupManager();
+ return ibm != null && ibm.isBackupServiceActive(UserHandle.USER_SYSTEM);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed requesting backup service state.", e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void setNetworkLoggingEnabled(ComponentName admin, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+ ensureDeviceOwnerManagingSingleUser(admin);
+
+ if (enabled == isNetworkLoggingEnabledInternalLocked()) {
+ // already in the requested state
+ return;
+ }
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ deviceOwner.isNetworkLoggingEnabled = enabled;
+ if (!enabled) {
+ deviceOwner.numNetworkLoggingNotifications = 0;
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ }
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+
+ setNetworkLoggingActiveInternal(enabled);
+ }
+
+ private synchronized void setNetworkLoggingActiveInternal(boolean active) {
+ final long callingIdentity = mInjector.binderClearCallingIdentity();
+ try {
+ if (active) {
+ mNetworkLogger = new NetworkLogger(this, mInjector.getPackageManagerInternal());
+ if (!mNetworkLogger.startNetworkLogging()) {
+ mNetworkLogger = null;
+ Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
+ + " service not being available yet.");
+ }
+ sendNetworkLoggingNotificationLocked();
+ } else {
+ if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
+ mNetworkLogger = null;
+ Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
+ + " service not being available yet.");
+ }
+ mNetworkLogger = null;
+ mInjector.getNotificationManager().cancel(NETWORK_LOGGING_NOTIFICATION_ID);
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public boolean isNetworkLoggingEnabled(ComponentName admin) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ enforceDeviceOwnerOrManageUsers();
+ return isNetworkLoggingEnabledInternalLocked();
+ }
+ }
+
+ private boolean isNetworkLoggingEnabledInternalLocked() {
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return (deviceOwner != null) && deviceOwner.isNetworkLoggingEnabled;
+ }
+
+ /*
+ * A maximum of 1200 events are returned, and the total marshalled size is in the order of
+ * 100kB, so returning a List instead of ParceledListSlice is acceptable.
+ * Ideally this would be done with ParceledList, however it only supports homogeneous types.
+ *
+ * @see NetworkLoggingHandler#MAX_EVENTS_PER_BATCH
+ */
+ @Override
+ public synchronized List<NetworkEvent> retrieveNetworkLogs(ComponentName admin,
+ long batchToken) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(admin);
+ ensureDeviceOwnerManagingSingleUser(admin);
+
+ if (mNetworkLogger == null) {
+ return null;
+ }
+ return isNetworkLoggingEnabledInternalLocked()
+ ? mNetworkLogger.retrieveLogs(batchToken)
+ : null;
+ }
+
+ private void sendNetworkLoggingNotificationLocked() {
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner == null || !deviceOwner.isNetworkLoggingEnabled) {
+ return;
+ }
+ if (deviceOwner.numNetworkLoggingNotifications >=
+ ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
+ return;
+ }
+ final long now = System.currentTimeMillis();
+ if (now - deviceOwner.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
+ return;
+ }
+ deviceOwner.numNetworkLoggingNotifications++;
+ if (deviceOwner.numNetworkLoggingNotifications
+ >= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ } else {
+ deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
+ }
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
+ intent.setPackage("com.android.systemui");
+ final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
+ UserHandle.CURRENT);
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_qs_network_logging)
+ .setContentTitle(mContext.getString(R.string.network_logging_notification_title))
+ .setContentText(mContext.getString(R.string.network_logging_notification_text))
+ .setTicker(mContext.getString(R.string.network_logging_notification_title))
+ .setShowWhen(true)
+ .setContentIntent(pendingIntent)
+ .build();
+ mInjector.getNotificationManager().notify(NETWORK_LOGGING_NOTIFICATION_ID, notification);
+ saveSettingsLocked(mOwners.getDeviceOwnerUserId());
+ }
}