/* * Copyright (C) 2010 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.server.devicepolicy; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; 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 static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.TEXT; 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.Nullable; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.IActivityManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.admin.DeviceAdminInfo; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.IDevicePolicyManager; import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; 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.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Color; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; import android.net.ProxyInfo; import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.Process; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.StorageManager; import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; import android.provider.Settings; import android.security.Credentials; import android.security.IKeyChainAliasCallback; import android.security.IKeyChainService; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; import android.service.persistentdata.PersistentDataBlockManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; import android.view.IWindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.ParcelableString; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo; import com.android.server.pm.UserRestrictionsUtils; import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** * Implementation of the device policy APIs. */ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String LOG_TAG = "DevicePolicyManagerService"; private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE private static final String DEVICE_POLICIES_XML = "device_policies.xml"; private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted-ca-certificate"; private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component"; private static final String TAG_STATUS_BAR = "statusbar"; private static final String ATTR_DISABLED = "disabled"; private static final String ATTR_NAME = "name"; private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML = "do-not-ask-credentials-on-boot"; private static final String TAG_AFFILIATION_ID = "affiliation-id"; private static final String TAG_ADMIN_BROADCAST_PENDING = "admin-broadcast-pending"; private static final String ATTR_VALUE = "value"; private static final String TAG_INITIALIZATION_BUNDLE = "initialization-bundle"; private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = 86400 * 1000; private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION"; 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 String ATTR_PERMISSION_PROVIDER = "permission-provider"; private static final String ATTR_SETUP_COMPLETE = "setup-complete"; private static final String ATTR_PROVISIONING_STATE = "provisioning-state"; 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_DELEGATED_CERT_INSTALLER = "delegated-cert-installer"; private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER = "application-restrictions-manager"; /** * System property whose value is either "true" or "false", indicating whether */ private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.device_owner"; private static final int STATUS_BAR_DISABLE_MASK = StatusBarManager.DISABLE_EXPAND | StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_ALERTS | StatusBarManager.DISABLE_SEARCH; private static final int STATUS_BAR_DISABLE2_MASK = StatusBarManager.DISABLE2_QUICK_SETTINGS; private static final Set SECURE_SETTINGS_WHITELIST; private static final Set SECURE_SETTINGS_DEVICEOWNER_WHITELIST; private static final Set GLOBAL_SETTINGS_WHITELIST; private static final Set GLOBAL_SETTINGS_DEPRECATED; static { SECURE_SETTINGS_WHITELIST = new ArraySet<>(); SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD); SECURE_SETTINGS_WHITELIST.add(Settings.Secure.SKIP_FIRST_USE_HINTS); SECURE_SETTINGS_WHITELIST.add(Settings.Secure.INSTALL_NON_MARKET_APPS); SECURE_SETTINGS_DEVICEOWNER_WHITELIST = new ArraySet<>(); SECURE_SETTINGS_DEVICEOWNER_WHITELIST.addAll(SECURE_SETTINGS_WHITELIST); SECURE_SETTINGS_DEVICEOWNER_WHITELIST.add(Settings.Secure.LOCATION_MODE); GLOBAL_SETTINGS_WHITELIST = new ArraySet<>(); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_SLEEP_POLICY); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN); GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN); GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>(); GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON); GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED); GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.MODE_RINGER); GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.NETWORK_PREFERENCE); GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.WIFI_ON); } /** * Keyguard features that when set on a managed profile that doesn't have its own challenge will * affect the profile's parent user. These can also be set on the managed profile's parent DPM * instance. */ private static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER = DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT; /** * Keyguard features that when set on a profile affect the profile content or challenge only. * These cannot be set on the managed profile's parent DPM instance */ private static final int PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY = DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; /** Keyguard features that are allowed to be set on a managed profile */ private static final int PROFILE_KEYGUARD_FEATURES = PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY; private static final int CODE_OK = 0; private static final int CODE_HAS_DEVICE_OWNER = 1; private static final int CODE_USER_HAS_PROFILE_OWNER = 2; private static final int CODE_USER_NOT_RUNNING = 3; private static final int CODE_USER_SETUP_COMPLETED = 4; 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; @Retention(RetentionPolicy.SOURCE) @IntDef({ CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING, CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER }) private @interface DeviceOwnerPreConditionCode {} 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; final UserManager mUserManager; final UserManagerInternal mUserManagerInternal; final TelephonyManager mTelephonyManager; private final LockPatternUtils mLockPatternUtils; /** * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p * is requested for user u. */ private final Set> mPackagesToRemove = new ArraySet>(); final LocalService mLocalService; // Stores and loads state on device and profile owners. @VisibleForTesting final Owners mOwners; private final Binder mToken = new Binder(); /** * Whether or not device admin feature is supported. If it isn't return defaults for all * public methods. */ boolean mHasFeature; private final SecurityLogMonitor mSecurityLogMonitor; private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean(); private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean(); private final Runnable mRemoteBugreportTimeoutRunnable = new Runnable() { @Override public void run() { if(mRemoteBugreportServiceIsActive.get()) { onBugreportFailed(); } } }; private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction()) && mRemoteBugreportServiceIsActive.get()) { onBugreportFinished(intent); } } }; private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); mInjector.getNotificationManager().cancel(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID); if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) { onBugreportSharingAccepted(); } else if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) { onBugreportSharingDeclined(); } mContext.unregisterReceiver(mRemoteBugreportConsentReceiver); } }; public static final class Lifecycle extends SystemService { private DevicePolicyManagerService mService; public Lifecycle(Context context) { super(context); mService = new DevicePolicyManagerService(context); } @Override public void onStart() { publishBinderService(Context.DEVICE_POLICY_SERVICE, mService); } @Override public void onBootPhase(int phase) { mService.systemReady(phase); } @Override public void onStartUser(int userHandle) { mService.onStartUser(userHandle); } } public static class DevicePolicyData { int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; int mActivePasswordLength = 0; int mActivePasswordUpperCase = 0; int mActivePasswordLowerCase = 0; int mActivePasswordLetters = 0; int mActivePasswordNumeric = 0; int mActivePasswordSymbols = 0; int mActivePasswordNonLetter = 0; int mFailedPasswordAttempts = 0; int mUserHandle; int mPasswordOwner = -1; long mLastMaximumTimeToLock = -1; boolean mUserSetupComplete = false; int mUserProvisioningState; int mPermissionPolicy; boolean mDeviceProvisioningConfigApplied = false; final ArrayMap mAdminMap = new ArrayMap<>(); final ArrayList mAdminList = new ArrayList<>(); final ArrayList mRemovingAdmins = new ArrayList<>(); final ArraySet mAcceptedCaCertificates = new ArraySet<>(); // This is the list of component allowed to start lock task mode. List mLockTaskPackages = new ArrayList<>(); boolean mStatusBarDisabled = false; ComponentName mRestrictionsProvider; String mDelegatedCertInstallerPackage; boolean doNotAskCredentialsOnBoot = false; String mApplicationRestrictionsManagingPackage; Set mAffiliationIds = new ArraySet<>(); // Used for initialization of users created by createAndManageUsers. boolean mAdminBroadcastPending = false; PersistableBundle mInitBundle = null; public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } } final SparseArray mUserData = new SparseArray<>(); final Handler mHandler; BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); if (Intent.ACTION_BOOT_COMPLETED.equals(action) && userHandle == mOwners.getDeviceOwnerUserId() && getDeviceOwnerRemoteBugreportUri() != null) { IntentFilter filterConsent = new IntentFilter(); filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED); filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED); mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent); mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID, RemoteBugreportUtils.buildNotification(mContext, DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED), UserHandle.ALL); } if (Intent.ACTION_BOOT_COMPLETED.equals(action) || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) { if (VERBOSE_LOG) { Slog.v(LOG_TAG, "Sending password expiration notifications for action " + action + " for user " + userHandle); } mHandler.post(new Runnable() { @Override public void run() { handlePasswordExpirationNotification(userHandle); } }); } if (Intent.ACTION_USER_UNLOCKED.equals(action) || Intent.ACTION_USER_STARTED.equals(action) || KeyChain.ACTION_STORAGE_CHANGED.equals(action)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL); new MonitoringCertNotificationTask().execute(userId); } if (Intent.ACTION_USER_ADDED.equals(action)) { disableDeviceOwnerManagedSingleUserFeaturesIfNeeded(); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { disableDeviceOwnerManagedSingleUserFeaturesIfNeeded(); removeUserData(userHandle); } else if (Intent.ACTION_USER_STARTED.equals(action)) { synchronized (DevicePolicyManagerService.this) { // Reset the policy data mUserData.remove(userHandle); sendAdminEnabledBroadcastLocked(userHandle); } handlePackagesChanged(null /* check all admins */, userHandle); } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { handlePackagesChanged(null /* check all admins */, userHandle); } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action) || (Intent.ACTION_PACKAGE_ADDED.equals(action) && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) { handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle); } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle); } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) { clearWipeProfileNotification(); } } }; 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_BLUETOOTH_CONTACT_SHARING = "disable-bt-contacts-sharing"; private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture"; 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_ACCOUNT_TYPE = "account-type"; private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES = "permitted-accessiblity-services"; private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested"; private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features"; private static final String TAG_TRUST_AGENT_COMPONENT_OPTIONS = "trust-agent-component-options"; private static final String TAG_TRUST_AGENT_COMPONENT = "component"; private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date"; private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout"; private static final String TAG_GLOBAL_PROXY_EXCLUSION_LIST = "global-proxy-exclusion-list"; private static final String TAG_GLOBAL_PROXY_SPEC = "global-proxy-spec"; private static final String TAG_SPECIFIES_GLOBAL_PROXY = "specifies-global-proxy"; 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_MIN_PASSWORD_LETTERS = "min-password-letters"; private static final String TAG_MIN_PASSWORD_LOWERCASE = "min-password-lowercase"; private static final String TAG_MIN_PASSWORD_UPPERCASE = "min-password-uppercase"; private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String ATTR_VALUE = "value"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = "cross-profile-widget-providers"; private static final String TAG_PROVIDER = "provider"; private static final String TAG_PACKAGE_LIST_ITEM = "item"; private static final String TAG_KEEP_UNINSTALLED_PACKAGES = "keep-uninstalled-packages"; private static final String TAG_USER_RESTRICTIONS = "user-restrictions"; private static final String TAG_SHORT_SUPPORT_MESSAGE = "short-support-message"; private static final String TAG_LONG_SUPPORT_MESSAGE = "long-support-message"; 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"; final DeviceAdminInfo info; int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; static final int DEF_MINIMUM_PASSWORD_LENGTH = 0; int minimumPasswordLength = DEF_MINIMUM_PASSWORD_LENGTH; static final int DEF_PASSWORD_HISTORY_LENGTH = 0; int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH; static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0; int minimumPasswordUpperCase = DEF_MINIMUM_PASSWORD_UPPER_CASE; static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0; int minimumPasswordLowerCase = DEF_MINIMUM_PASSWORD_LOWER_CASE; static final int DEF_MINIMUM_PASSWORD_LETTERS = 1; int minimumPasswordLetters = DEF_MINIMUM_PASSWORD_LETTERS; static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1; int minimumPasswordNumeric = DEF_MINIMUM_PASSWORD_NUMERIC; static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1; int minimumPasswordSymbols = DEF_MINIMUM_PASSWORD_SYMBOLS; static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0; int minimumPasswordNonLetter = DEF_MINIMUM_PASSWORD_NON_LETTER; 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; static final long DEF_PASSWORD_EXPIRATION_TIMEOUT = 0; long passwordExpirationTimeout = DEF_PASSWORD_EXPIRATION_TIMEOUT; static final long DEF_PASSWORD_EXPIRATION_DATE = 0; long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE; static final int DEF_KEYGUARD_FEATURES_DISABLED = 0; // none int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED; boolean encryptionRequested = false; boolean testOnlyAdmin = false; boolean disableCamera = false; boolean disableCallerId = false; boolean disableContactsSearch = false; boolean disableBluetoothContactSharing = true; 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. ActiveAdmin parentAdmin; final boolean isParent; static class TrustAgentInfo { public PersistableBundle options; TrustAgentInfo(PersistableBundle bundle) { options = bundle; } } Set accountTypesWithManagementDisabled = new ArraySet<>(); // The list of permitted accessibility services package namesas set by a profile // or device owner. Null means all accessibility services are allowed, empty means // none except system services are allowed. List permittedAccessiblityServices; // The list of permitted input methods package names as set by a profile or device owner. // Null means all input methods are allowed, empty means none except system imes are // allowed. List permittedInputMethods; // List of package names to keep cached. List keepUninstalledPackages; // TODO: review implementation decisions with frameworks team boolean specifiesGlobalProxy = false; String globalProxySpec = null; String globalProxyExclusionList = null; ArrayMap trustAgentInfos = new ArrayMap<>(); List crossProfileWidgetProviders; Bundle userRestrictions; // Support text provided by the admin to display to the user. CharSequence shortSupportMessage = null; CharSequence longSupportMessage = null; // Background color of confirm credentials screen. Default: teal. static final int DEF_ORGANIZATION_COLOR = Color.parseColor("#00796B"); int organizationColor = DEF_ORGANIZATION_COLOR; // Default title of confirm credentials screen String organizationName = null; ActiveAdmin(DeviceAdminInfo _info, boolean parent) { info = _info; isParent = parent; } ActiveAdmin getParentActiveAdmin() { Preconditions.checkState(!isParent); if (parentAdmin == null) { parentAdmin = new ActiveAdmin(info, /* parent */ true); } return parentAdmin; } boolean hasParentActiveAdmin() { return parentAdmin != null; } int getUid() { return info.getActivityInfo().applicationInfo.uid; } public UserHandle getUserHandle() { return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid)); } void writeToXml(XmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { out.startTag(null, TAG_POLICIES); info.writePoliciesToXml(out); out.endTag(null, TAG_POLICIES); if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { out.startTag(null, TAG_PASSWORD_QUALITY); out.attribute(null, ATTR_VALUE, Integer.toString(passwordQuality)); out.endTag(null, TAG_PASSWORD_QUALITY); if (minimumPasswordLength != DEF_MINIMUM_PASSWORD_LENGTH) { out.startTag(null, TAG_MIN_PASSWORD_LENGTH); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLength)); out.endTag(null, TAG_MIN_PASSWORD_LENGTH); } if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) { out.startTag(null, TAG_PASSWORD_HISTORY_LENGTH); out.attribute(null, ATTR_VALUE, Integer.toString(passwordHistoryLength)); out.endTag(null, TAG_PASSWORD_HISTORY_LENGTH); } if (minimumPasswordUpperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) { out.startTag(null, TAG_MIN_PASSWORD_UPPERCASE); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordUpperCase)); out.endTag(null, TAG_MIN_PASSWORD_UPPERCASE); } if (minimumPasswordLowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) { out.startTag(null, TAG_MIN_PASSWORD_LOWERCASE); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLowerCase)); out.endTag(null, TAG_MIN_PASSWORD_LOWERCASE); } if (minimumPasswordLetters != DEF_MINIMUM_PASSWORD_LETTERS) { out.startTag(null, TAG_MIN_PASSWORD_LETTERS); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordLetters)); out.endTag(null, TAG_MIN_PASSWORD_LETTERS); } if (minimumPasswordNumeric != DEF_MINIMUM_PASSWORD_NUMERIC) { out.startTag(null, TAG_MIN_PASSWORD_NUMERIC); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordNumeric)); out.endTag(null, TAG_MIN_PASSWORD_NUMERIC); } if (minimumPasswordSymbols != DEF_MINIMUM_PASSWORD_SYMBOLS) { out.startTag(null, TAG_MIN_PASSWORD_SYMBOLS); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordSymbols)); out.endTag(null, TAG_MIN_PASSWORD_SYMBOLS); } if (minimumPasswordNonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) { out.startTag(null, TAG_MIN_PASSWORD_NONLETTER); out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordNonLetter)); out.endTag(null, TAG_MIN_PASSWORD_NONLETTER); } } if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) { out.startTag(null, TAG_MAX_TIME_TO_UNLOCK); 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.endTag(null, TAG_MAX_FAILED_PASSWORD_WIPE); } if (specifiesGlobalProxy) { out.startTag(null, TAG_SPECIFIES_GLOBAL_PROXY); out.attribute(null, ATTR_VALUE, Boolean.toString(specifiesGlobalProxy)); out.endTag(null, TAG_SPECIFIES_GLOBAL_PROXY); if (globalProxySpec != null) { out.startTag(null, TAG_GLOBAL_PROXY_SPEC); out.attribute(null, ATTR_VALUE, globalProxySpec); out.endTag(null, TAG_GLOBAL_PROXY_SPEC); } if (globalProxyExclusionList != null) { out.startTag(null, TAG_GLOBAL_PROXY_EXCLUSION_LIST); out.attribute(null, ATTR_VALUE, globalProxyExclusionList); out.endTag(null, TAG_GLOBAL_PROXY_EXCLUSION_LIST); } } if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) { out.startTag(null, TAG_PASSWORD_EXPIRATION_TIMEOUT); out.attribute(null, ATTR_VALUE, Long.toString(passwordExpirationTimeout)); out.endTag(null, TAG_PASSWORD_EXPIRATION_TIMEOUT); } if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) { out.startTag(null, TAG_PASSWORD_EXPIRATION_DATE); out.attribute(null, ATTR_VALUE, Long.toString(passwordExpirationDate)); out.endTag(null, TAG_PASSWORD_EXPIRATION_DATE); } if (encryptionRequested) { out.startTag(null, TAG_ENCRYPTION_REQUESTED); 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.endTag(null, TAG_DISABLE_CAMERA); } if (disableCallerId) { out.startTag(null, TAG_DISABLE_CALLER_ID); out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId)); out.endTag(null, TAG_DISABLE_CALLER_ID); } if (disableContactsSearch) { out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH); out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch)); out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH); } if (!disableBluetoothContactSharing) { out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING); out.attribute(null, ATTR_VALUE, Boolean.toString(disableBluetoothContactSharing)); out.endTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING); } if (disableScreenCapture) { out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE); out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture)); out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE); } if (requireAutoTime) { out.startTag(null, TAG_REQUIRE_AUTO_TIME); out.attribute(null, ATTR_VALUE, Boolean.toString(requireAutoTime)); out.endTag(null, TAG_REQUIRE_AUTO_TIME); } if (forceEphemeralUsers) { out.startTag(null, TAG_FORCE_EPHEMERAL_USERS); out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers)); out.endTag(null, TAG_FORCE_EPHEMERAL_USERS); } if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) { out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES); out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures)); out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES); } if (!accountTypesWithManagementDisabled.isEmpty()) { out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT); for (String ac : accountTypesWithManagementDisabled) { out.startTag(null, TAG_ACCOUNT_TYPE); out.attribute(null, ATTR_VALUE, ac); out.endTag(null, TAG_ACCOUNT_TYPE); } out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT); } if (!trustAgentInfos.isEmpty()) { Set> set = trustAgentInfos.entrySet(); out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES); for (Entry entry : set) { TrustAgentInfo trustAgentInfo = entry.getValue(); out.startTag(null, TAG_TRUST_AGENT_COMPONENT); out.attribute(null, ATTR_VALUE, entry.getKey()); if (trustAgentInfo.options != null) { out.startTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS); try { trustAgentInfo.options.saveToXml(out); } catch (XmlPullParserException e) { Log.e(LOG_TAG, "Failed to save TrustAgent options", e); } out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS); } out.endTag(null, TAG_TRUST_AGENT_COMPONENT); } out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES); } if (crossProfileWidgetProviders != null && !crossProfileWidgetProviders.isEmpty()) { out.startTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS); final int providerCount = crossProfileWidgetProviders.size(); for (int i = 0; i < providerCount; i++) { String provider = crossProfileWidgetProviders.get(i); out.startTag(null, TAG_PROVIDER); out.attribute(null, ATTR_VALUE, provider); out.endTag(null, TAG_PROVIDER); } out.endTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS); } writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES, permittedAccessiblityServices); writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods); writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages); if (hasUserRestrictions()) { UserRestrictionsUtils.writeRestrictions( out, userRestrictions, TAG_USER_RESTRICTIONS); } if (!TextUtils.isEmpty(shortSupportMessage)) { out.startTag(null, TAG_SHORT_SUPPORT_MESSAGE); out.text(shortSupportMessage.toString()); out.endTag(null, TAG_SHORT_SUPPORT_MESSAGE); } if (!TextUtils.isEmpty(longSupportMessage)) { out.startTag(null, TAG_LONG_SUPPORT_MESSAGE); out.text(longSupportMessage.toString()); out.endTag(null, TAG_LONG_SUPPORT_MESSAGE); } if (parentAdmin != null) { out.startTag(null, TAG_PARENT_ADMIN); parentAdmin.writeToXml(out); out.endTag(null, TAG_PARENT_ADMIN); } if (organizationColor != DEF_ORGANIZATION_COLOR) { out.startTag(null, TAG_ORGANIZATION_COLOR); out.attribute(null, ATTR_VALUE, Integer.toString(organizationColor)); out.endTag(null, TAG_ORGANIZATION_COLOR); } if (organizationName != null) { out.startTag(null, TAG_ORGANIZATION_NAME); out.text(organizationName); out.endTag(null, TAG_ORGANIZATION_NAME); } } void writePackageListToXml(XmlSerializer out, String outerTag, List packageList) throws IllegalArgumentException, IllegalStateException, IOException { if (packageList == null) { return; } out.startTag(null, outerTag); for (String packageName : packageList) { out.startTag(null, TAG_PACKAGE_LIST_ITEM); out.attribute(null, ATTR_VALUE, packageName); out.endTag(null, TAG_PACKAGE_LIST_ITEM); } out.endTag(null, outerTag); } void readFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != END_DOCUMENT && (type != END_TAG || parser.getDepth() > outerDepth)) { if (type == END_TAG || type == TEXT) { continue; } String tag = parser.getName(); if (TAG_POLICIES.equals(tag)) { info.readPoliciesFromXml(parser); } else if (TAG_PASSWORD_QUALITY.equals(tag)) { passwordQuality = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) { minimumPasswordLength = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) { passwordHistoryLength = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) { minimumPasswordUpperCase = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) { minimumPasswordLowerCase = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) { minimumPasswordLetters = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) { minimumPasswordNumeric = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) { minimumPasswordSymbols = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { minimumPasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } 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_SPECIFIES_GLOBAL_PROXY.equals(tag)) { specifiesGlobalProxy = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_GLOBAL_PROXY_SPEC.equals(tag)) { globalProxySpec = parser.getAttributeValue(null, ATTR_VALUE); } else if (TAG_GLOBAL_PROXY_EXCLUSION_LIST.equals(tag)) { globalProxyExclusionList = parser.getAttributeValue(null, ATTR_VALUE); } else if (TAG_PASSWORD_EXPIRATION_TIMEOUT.equals(tag)) { passwordExpirationTimeout = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_PASSWORD_EXPIRATION_DATE.equals(tag)) { passwordExpirationDate = Long.parseLong( 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_DISABLE_CALLER_ID.equals(tag)) { disableCallerId = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) { disableContactsSearch = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) { disableBluetoothContactSharing = Boolean.parseBoolean(parser .getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) { disableScreenCapture = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_REQUIRE_AUTO_TIME.equals(tag)) { requireAutoTime = 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_DISABLE_KEYGUARD_FEATURES.equals(tag)) { disabledKeyguardFeatures = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) { accountTypesWithManagementDisabled = readDisableAccountInfo(parser, tag); } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) { trustAgentInfos = getAllTrustAgentInfos(parser, tag); } else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) { crossProfileWidgetProviders = getCrossProfileWidgetProviders(parser, tag); } else if (TAG_PERMITTED_ACCESSIBILITY_SERVICES.equals(tag)) { permittedAccessiblityServices = readPackageList(parser, tag); } else if (TAG_PERMITTED_IMES.equals(tag)) { permittedInputMethods = readPackageList(parser, tag); } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) { keepUninstalledPackages = readPackageList(parser, tag); } else if (TAG_USER_RESTRICTIONS.equals(tag)) { UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions()); } else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { shortSupportMessage = parser.getText(); } else { Log.w(LOG_TAG, "Missing text when loading short support message"); } } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { longSupportMessage = parser.getText(); } else { Log.w(LOG_TAG, "Missing text when loading long support message"); } } else if (TAG_PARENT_ADMIN.equals(tag)) { Preconditions.checkState(!isParent); parentAdmin = new ActiveAdmin(info, /* parent */ true); parentAdmin.readFromXml(parser); } else if (TAG_ORGANIZATION_COLOR.equals(tag)) { organizationColor = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_ORGANIZATION_NAME.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { organizationName = parser.getText(); } else { Log.w(LOG_TAG, "Missing text when loading organization name"); } } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); } } } private List readPackageList(XmlPullParser parser, String tag) throws XmlPullParserException, IOException { List result = new ArrayList(); int outerDepth = parser.getDepth(); int outerType; while ((outerType=parser.next()) != XmlPullParser.END_DOCUMENT && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) { continue; } String outerTag = parser.getName(); if (TAG_PACKAGE_LIST_ITEM.equals(outerTag)) { String packageName = parser.getAttributeValue(null, ATTR_VALUE); if (packageName != null) { result.add(packageName); } else { Slog.w(LOG_TAG, "Package name missing under " + outerTag); } } else { Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + outerTag); } } return result; } private Set readDisableAccountInfo(XmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; Set result = new ArraySet<>(); while ((typeDAM=parser.next()) != END_DOCUMENT && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { if (typeDAM == END_TAG || typeDAM == TEXT) { continue; } String tagDAM = parser.getName(); if (TAG_ACCOUNT_TYPE.equals(tagDAM)) { result.add(parser.getAttributeValue(null, ATTR_VALUE)); } else { Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); } } return result; } private ArrayMap getAllTrustAgentInfos( XmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; final ArrayMap result = new ArrayMap<>(); while ((typeDAM=parser.next()) != END_DOCUMENT && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { if (typeDAM == END_TAG || typeDAM == TEXT) { continue; } String tagDAM = parser.getName(); if (TAG_TRUST_AGENT_COMPONENT.equals(tagDAM)) { final String component = parser.getAttributeValue(null, ATTR_VALUE); final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag); result.put(component, trustAgentInfo); } else { Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); } } return result; } private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; TrustAgentInfo result = new TrustAgentInfo(null); while ((typeDAM=parser.next()) != END_DOCUMENT && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { if (typeDAM == END_TAG || typeDAM == TEXT) { continue; } String tagDAM = parser.getName(); if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) { result.options = PersistableBundle.restoreFromXml(parser); } else { Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); } } return result; } private List getCrossProfileWidgetProviders(XmlPullParser parser, String tag) throws XmlPullParserException, IOException { int outerDepthDAM = parser.getDepth(); int typeDAM; ArrayList result = null; while ((typeDAM=parser.next()) != END_DOCUMENT && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { if (typeDAM == END_TAG || typeDAM == TEXT) { continue; } String tagDAM = parser.getName(); if (TAG_PROVIDER.equals(tagDAM)) { final String provider = parser.getAttributeValue(null, ATTR_VALUE); if (result == null) { result = new ArrayList<>(); } result.add(provider); } else { Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); } } return result; } boolean hasUserRestrictions() { return userRestrictions != null && userRestrictions.size() > 0; } Bundle ensureUserRestrictions() { if (userRestrictions == null) { userRestrictions = new Bundle(); } return userRestrictions; } 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 pols = info.getUsedPolicies(); if (pols != null) { for (int i=0; i= 0; i--) { ActiveAdmin aa = policy.mAdminList.get(i); try { // If we're checking all packages or if the specific one we're checking matches, // then check if the package and receiver still exist. final String adminPackage = aa.info.getPackageName(); if (packageName == null || packageName.equals(adminPackage)) { if (mIPackageManager.getPackageInfo(adminPackage, 0, userHandle) == null || mIPackageManager.getReceiverInfo(aa.info.getComponent(), PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle) == null) { removed = true; policy.mAdminList.remove(i); policy.mAdminMap.remove(aa.info.getComponent()); } } } catch (RemoteException re) { // Shouldn't happen } } if (removed) { validatePasswordOwnerLocked(policy); saveSettingsLocked(policy.mUserHandle); } // Check if delegated cert installer or app restrictions managing packages are removed. if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) { policy.mDelegatedCertInstallerPackage = null; saveSettingsLocked(policy.mUserHandle); } if (isRemovedPackage( packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) { policy.mApplicationRestrictionsManagingPackage = null; saveSettingsLocked(policy.mUserHandle); } } if (removed) { // The removed admin might have disabled camera, so update user restrictions. pushUserRestrictions(userHandle); } } private boolean isRemovedPackage(String changedPackage, String targetPackage, int userHandle) { try { return targetPackage != null && (changedPackage == null || changedPackage.equals(targetPackage)) && mIPackageManager.getPackageInfo(targetPackage, 0, userHandle) == null; } catch (RemoteException e) { // Shouldn't happen } return false; } /** * Unit test will subclass it to inject mocks. */ @VisibleForTesting static class Injector { private final Context mContext; Injector(Context context) { mContext = context; } Owners newOwners() { return new Owners(getUserManager(), getUserManagerInternal(), getPackageManagerInternal()); } UserManager getUserManager() { return UserManager.get(mContext); } UserManagerInternal getUserManagerInternal() { return LocalServices.getService(UserManagerInternal.class); } PackageManagerInternal getPackageManagerInternal() { return LocalServices.getService(PackageManagerInternal.class); } NotificationManager getNotificationManager() { return mContext.getSystemService(NotificationManager.class); } PowerManagerInternal getPowerManagerInternal() { return LocalServices.getService(PowerManagerInternal.class); } TelephonyManager getTelephonyManager() { return TelephonyManager.from(mContext); } TrustManager getTrustManager() { return (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); } IWindowManager getIWindowManager() { return IWindowManager.Stub .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)); } IActivityManager getIActivityManager() { return ActivityManagerNative.getDefault(); } IPackageManager getIPackageManager() { return AppGlobals.getPackageManager(); } IBackupManager getIBackupManager() { return IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); } IAudioService getIAudioService() { return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE)); } LockPatternUtils newLockPatternUtils() { return new LockPatternUtils(mContext); } boolean storageManagerIsFileBasedEncryptionEnabled() { return StorageManager.isFileEncryptedNativeOnly(); } boolean storageManagerIsNonDefaultBlockEncrypted() { long identity = Binder.clearCallingIdentity(); try { return StorageManager.isNonDefaultBlockEncrypted(); } finally { Binder.restoreCallingIdentity(identity); } } boolean storageManagerIsEncrypted() { return StorageManager.isEncrypted(); } boolean storageManagerIsEncryptable() { return StorageManager.isEncryptable(); } Looper getMyLooper() { return Looper.myLooper(); } WifiManager getWifiManager() { return mContext.getSystemService(WifiManager.class); } long binderClearCallingIdentity() { return Binder.clearCallingIdentity(); } void binderRestoreCallingIdentity(long token) { Binder.restoreCallingIdentity(token); } int binderGetCallingUid() { return Binder.getCallingUid(); } int binderGetCallingPid() { return Binder.getCallingPid(); } UserHandle binderGetCallingUserHandle() { return Binder.getCallingUserHandle(); } boolean binderIsCallingUidMyUid() { return getCallingUid() == Process.myUid(); } final int userHandleGetCallingUserId() { return UserHandle.getUserId(binderGetCallingUid()); } File environmentGetUserSystemDirectory(int userId) { return Environment.getUserSystemDirectory(userId); } void powerManagerGoToSleep(long time, int reason, int flags) { mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags); } void powerManagerReboot(String reason) { mContext.getSystemService(PowerManager.class).reboot(reason); } boolean systemPropertiesGetBoolean(String key, boolean def) { return SystemProperties.getBoolean(key, def); } long systemPropertiesGetLong(String key, long def) { return SystemProperties.getLong(key, def); } String systemPropertiesGet(String key, String def) { return SystemProperties.get(key, def); } String systemPropertiesGet(String key) { return SystemProperties.get(key); } void systemPropertiesSet(String key, String value) { SystemProperties.set(key, value); } boolean userManagerIsSplitSystemUser() { return UserManager.isSplitSystemUser(); } String getDevicePolicyFilePathForSystemUser() { return "/data/system/"; } void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer, int userHandle) { mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents, observer, userHandle); } int settingsSecureGetIntForUser(String name, int def, int userHandle) { return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, def, userHandle); } void settingsSecurePutIntForUser(String name, int value, int userHandle) { Settings.Secure.putIntForUser(mContext.getContentResolver(), name, value, userHandle); } void settingsSecurePutStringForUser(String name, String value, int userHandle) { Settings.Secure.putStringForUser(mContext.getContentResolver(), name, value, userHandle); } void settingsGlobalPutStringForUser(String name, String value, int userHandle) { Settings.Global.putStringForUser(mContext.getContentResolver(), name, value, userHandle); } void settingsSecurePutInt(String name, int value) { Settings.Secure.putInt(mContext.getContentResolver(), name, value); } int settingsGlobalGetInt(String name, int def) { return Settings.Global.getInt(mContext.getContentResolver(), name, def); } void settingsGlobalPutInt(String name, int value) { Settings.Global.putInt(mContext.getContentResolver(), name, value); } void settingsSecurePutString(String name, String value) { Settings.Secure.putString(mContext.getContentResolver(), name, value); } void settingsGlobalPutString(String name, String value) { Settings.Global.putString(mContext.getContentResolver(), name, value); } void securityLogSetLoggingEnabledProperty(boolean enabled) { SecurityLog.setLoggingEnabledProperty(enabled); } boolean securityLogGetLoggingEnabledProperty() { return SecurityLog.getLoggingEnabledProperty(); } boolean securityLogIsLoggingEnabled() { return SecurityLog.isLoggingEnabled(); } } /** * Instantiates the service. */ public DevicePolicyManagerService(Context context) { this(new Injector(context)); } @VisibleForTesting DevicePolicyManagerService(Injector injector) { mInjector = injector; mContext = Preconditions.checkNotNull(injector.mContext); mHandler = new Handler(Preconditions.checkNotNull(injector.getMyLooper())); mOwners = Preconditions.checkNotNull(injector.newOwners()); mUserManager = Preconditions.checkNotNull(injector.getUserManager()); mUserManagerInternal = Preconditions.checkNotNull(injector.getUserManagerInternal()); mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager()); mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager()); mLocalService = new LocalService(); mLockPatternUtils = injector.newLockPatternUtils(); mSecurityLogMonitor = new SecurityLogMonitor(this); mHasFeature = mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN); if (!mHasFeature) { // Skip the rest of the initialization return; } IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BOOT_COMPLETED); filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(KeyChain.ACTION_STORAGE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addDataScheme("package"); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService); } /** * Creates and loads the policy data from xml. * @param userHandle the user for whom to load the policy data * @return */ @NonNull DevicePolicyData getUserData(int userHandle) { synchronized (this) { DevicePolicyData policy = mUserData.get(userHandle); if (policy == null) { policy = new DevicePolicyData(userHandle); mUserData.append(userHandle, policy); loadSettingsLocked(policy, userHandle); } return policy; } } /** * Creates and loads the policy data from xml for data that is shared between * various profiles of a user. In contrast to {@link #getUserData(int)} * it allows access to data of users other than the calling user. * * This function should only be used for shared data, e.g. everything regarding * passwords and should be removed once multiple screen locks are present. * @param userHandle the user for whom to load the policy data * @return */ DevicePolicyData getUserDataUnchecked(int userHandle) { long ident = mInjector.binderClearCallingIdentity(); try { return getUserData(userHandle); } finally { mInjector.binderRestoreCallingIdentity(ident); } } void removeUserData(int userHandle) { synchronized (this) { if (userHandle == UserHandle.USER_SYSTEM) { Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring."); return; } mOwners.removeProfileOwner(userHandle); mOwners.writeProfileOwner(userHandle); DevicePolicyData policy = mUserData.get(userHandle); if (policy != null) { mUserData.remove(userHandle); } File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle), DEVICE_POLICIES_XML); policyFile.delete(); Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath()); } updateScreenCaptureDisabledInWindowManager(userHandle, false /* default value */); } void loadOwners() { synchronized (this) { mOwners.load(); setDeviceOwnerSystemPropertyLocked(); findOwnerComponentIfNecessaryLocked(); migrateUserRestrictionsIfNecessaryLocked(); // TODO PO may not have a class name either due to b/17652534. Address that too. updateDeviceOwnerLocked(); } } 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) { return; } // Still at the first stage of CryptKeeper double bounce, mOwners.hasDeviceOwner is // always false at this point. if (StorageManager.inCryptKeeperBounce()) { return; } if (!TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT))) { Slog.w(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?"); } else { if (mOwners.hasDeviceOwner()) { mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "true"); Slog.i(LOG_TAG, "Set ro.device_owner property to true"); disableDeviceOwnerManagedSingleUserFeaturesIfNeeded(); if (mInjector.securityLogGetLoggingEnabledProperty()) { mSecurityLogMonitor.start(); } } else { mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "false"); Slog.i(LOG_TAG, "Set ro.device_owner property to false"); } } } private void findOwnerComponentIfNecessaryLocked() { if (!mOwners.hasDeviceOwner()) { return; } final ComponentName doComponentName = mOwners.getDeviceOwnerComponent(); if (!TextUtils.isEmpty(doComponentName.getClassName())) { return; // Already a full component name. } final ComponentName doComponent = findAdminComponentWithPackageLocked( doComponentName.getPackageName(), mOwners.getDeviceOwnerUserId()); if (doComponent == null) { Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin"); } else { mOwners.setDeviceOwnerWithRestrictionsMigrated( doComponent, mOwners.getDeviceOwnerName(), mOwners.getDeviceOwnerUserId(), !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); mOwners.writeDeviceOwner(); if (VERBOSE_LOG) { Log.v(LOG_TAG, "Device owner component filled in"); } } } /** * We didn't use to persist user restrictions for each owners but only persisted in user * manager. */ private void migrateUserRestrictionsIfNecessaryLocked() { boolean migrated = false; // Migrate for the DO. Basically all restrictions should be considered to be set by DO, // except for the "system controlled" ones. if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) { if (VERBOSE_LOG) { Log.v(LOG_TAG, "Migrating DO user restrictions"); } migrated = true; // Migrate user 0 restrictions to DO. final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); migrateUserRestrictionsForUser(UserHandle.SYSTEM, deviceOwnerAdmin, /* exceptionList =*/ null, /* isDeviceOwner =*/ true); // Push DO user restrictions to user manager. pushUserRestrictions(UserHandle.USER_SYSTEM); mOwners.setDeviceOwnerUserRestrictionsMigrated(); } // Migrate for POs. // The following restrictions can be set on secondary users by the device owner, so we // assume they're not from the PO. final Set secondaryUserExceptionList = Sets.newArraySet( UserManager.DISALLOW_OUTGOING_CALLS, UserManager.DISALLOW_SMS); for (UserInfo ui : mUserManager.getUsers()) { final int userId = ui.id; if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) { if (VERBOSE_LOG) { Log.v(LOG_TAG, "Migrating PO user restrictions for user " + userId); } migrated = true; final ActiveAdmin profileOwnerAdmin = getProfileOwnerAdminLocked(userId); final Set exceptionList = (userId == UserHandle.USER_SYSTEM) ? null : secondaryUserExceptionList; migrateUserRestrictionsForUser(ui.getUserHandle(), profileOwnerAdmin, exceptionList, /* isDeviceOwner =*/ false); // Note if a secondary user has no PO but has a DA that disables camera, we // don't get here and won't push the camera user restriction to UserManager // here. That's okay because we'll push user restrictions anyway when a user // starts. But we still do it because we want to let user manager persist // upon migration. pushUserRestrictions(userId); mOwners.setProfileOwnerUserRestrictionsMigrated(userId); } } if (VERBOSE_LOG && migrated) { Log.v(LOG_TAG, "User restrictions migrated."); } } private void migrateUserRestrictionsForUser(UserHandle user, ActiveAdmin admin, Set exceptionList, boolean isDeviceOwner) { final Bundle origRestrictions = mUserManagerInternal.getBaseUserRestrictions( user.getIdentifier()); final Bundle newBaseRestrictions = new Bundle(); final Bundle newOwnerRestrictions = new Bundle(); for (String key : origRestrictions.keySet()) { if (!origRestrictions.getBoolean(key)) { continue; } final boolean canOwnerChange = isDeviceOwner ? UserRestrictionsUtils.canDeviceOwnerChange(key) : UserRestrictionsUtils.canProfileOwnerChange(key, user.getIdentifier()); if (!canOwnerChange || (exceptionList!= null && exceptionList.contains(key))) { newBaseRestrictions.putBoolean(key, true); } else { newOwnerRestrictions.putBoolean(key, true); } } if (VERBOSE_LOG) { Log.v(LOG_TAG, "origRestrictions=" + origRestrictions); Log.v(LOG_TAG, "newBaseRestrictions=" + newBaseRestrictions); Log.v(LOG_TAG, "newOwnerRestrictions=" + newOwnerRestrictions); } mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(), newBaseRestrictions); if (admin != null) { admin.ensureUserRestrictions().clear(); admin.ensureUserRestrictions().putAll(newOwnerRestrictions); } else { Slog.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier()); } saveSettingsLocked(user.getIdentifier()); } private ComponentName findAdminComponentWithPackageLocked(String packageName, int userId) { final DevicePolicyData policy = getUserData(userId); final int n = policy.mAdminList.size(); ComponentName found = null; int nFound = 0; for (int i = 0; i < n; i++) { final ActiveAdmin admin = policy.mAdminList.get(i); if (packageName.equals(admin.info.getPackageName())) { // Found! if (nFound == 0) { found = admin.info.getComponent(); } nFound++; } } if (nFound > 1) { Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO."); } return found; } /** * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration * reminders. Clears alarm if no expirations are configured. */ private void setExpirationAlarmCheckLocked(Context context, int userHandle, boolean parent) { final long expiration = getPasswordExpirationLocked(null, userHandle, parent); final long now = System.currentTimeMillis(); final long timeToExpire = expiration - now; final long alarmTime; if (expiration == 0) { // No expirations are currently configured: Cancel alarm. alarmTime = 0; } else if (timeToExpire <= 0) { // The password has already expired: Repeat every 24 hours. alarmTime = now + MS_PER_DAY; } else { // Selecting the next alarm time: Roll forward to the next 24 hour multiple before // the expiration time. long alarmInterval = timeToExpire % MS_PER_DAY; if (alarmInterval == 0) { alarmInterval = MS_PER_DAY; } alarmTime = now + alarmInterval; } long token = mInjector.binderClearCallingIdentity(); try { int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle; AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD, new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.of(affectedUserHandle)); am.cancel(pi); if (alarmTime != 0) { am.set(AlarmManager.RTC, alarmTime, pi); } } finally { mInjector.binderRestoreCallingIdentity(token); } } ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle) { ActiveAdmin admin = getUserData(userHandle).mAdminMap.get(who); if (admin != null && who.getPackageName().equals(admin.info.getActivityInfo().packageName) && who.getClassName().equals(admin.info.getActivityInfo().name)) { return admin; } return null; } ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) { if (parent) { enforceManagedProfile(userHandle, "call APIs on the parent profile"); } ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin != null && parent) { admin = admin.getParentActiveAdmin(); } return admin; } ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy) throws SecurityException { final int callingUid = mInjector.binderGetCallingUid(); ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid); if (result != null) { return result; } if (who != null) { final int userId = UserHandle.getUserId(callingUid); final DevicePolicyData policy = getUserData(userId); ActiveAdmin admin = policy.mAdminMap.get(who); if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { throw new SecurityException("Admin " + admin.info.getComponent() + " does not own the device"); } if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { throw new SecurityException("Admin " + admin.info.getComponent() + " does not own the profile"); } throw new SecurityException("Admin " + admin.info.getComponent() + " did not specify uses-policy for: " + admin.info.getTagForPolicy(reqPolicy)); } else { throw new SecurityException("No active admin owned by uid " + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy); } } ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy, boolean parent) throws SecurityException { if (parent) { enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "call APIs on the parent profile"); } ActiveAdmin admin = getActiveAdminForCallerLocked(who, reqPolicy); return parent ? admin.getParentActiveAdmin() : admin; } /** * Find the admin for the component and userId bit of the uid, then check * the admin's uid matches the uid. */ private ActiveAdmin getActiveAdminForUidLocked(ComponentName who, int uid) { final int userId = UserHandle.getUserId(uid); final DevicePolicyData policy = getUserData(userId); ActiveAdmin admin = policy.mAdminMap.get(who); if (admin == null) { throw new SecurityException("No active admin " + who); } if (admin.getUid() != uid) { throw new SecurityException("Admin " + who + " is not owned by uid " + uid); } return admin; } private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy, int uid) { // Try to find an admin which can use reqPolicy final int userId = UserHandle.getUserId(uid); final DevicePolicyData policy = getUserData(userId); if (who != null) { ActiveAdmin admin = policy.mAdminMap.get(who); if (admin == null) { throw new SecurityException("No active admin " + who); } if (admin.getUid() != uid) { throw new SecurityException("Admin " + who + " is not owned by uid " + uid); } if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) { return admin; } } else { for (ActiveAdmin admin : policy.mAdminList) { if (admin.getUid() == uid && isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) { return admin; } } } return null; } @VisibleForTesting boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy, int userId) { final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userId); final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userId); if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { return ownsDevice; } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { // DO always has the PO power. return ownsDevice || ownsProfile; } else { return admin.info.usesPolicy(reqPolicy); } } void sendAdminCommandLocked(ActiveAdmin admin, String action) { sendAdminCommandLocked(admin, action, null); } void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) { sendAdminCommandLocked(admin, action, null, result); } /** * Send an update to one specific admin, get notified when that admin returns a result. */ void sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras, BroadcastReceiver result) { Intent intent = new Intent(action); intent.setComponent(admin.info.getComponent()); if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) { intent.putExtra("expiration", admin.passwordExpirationDate); } if (adminExtras != null) { intent.putExtras(adminExtras); } if (result != null) { mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(), null, result, mHandler, Activity.RESULT_OK, null, null); } else { mContext.sendBroadcastAsUser(intent, admin.getUserHandle()); } } /** * Send an update to all admins of a user that enforce a specified policy. */ void sendAdminCommandLocked(String action, int reqPolicy, int userHandle) { final DevicePolicyData policy = getUserData(userHandle); final int count = policy.mAdminList.size(); if (count > 0) { for (int i = 0; i < count; i++) { final ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(reqPolicy)) { sendAdminCommandLocked(admin, action); } } } } /** * Send an update intent to all admins of a user and its profiles. Only send to admins that * enforce a specified policy. */ private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy, int userHandle) { int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle); for (int profileId : profileIds) { sendAdminCommandLocked(action, reqPolicy, profileId); } } /** * Sends a broadcast to each profile that share the password unlock with the given user id. */ private void sendAdminCommandForLockscreenPoliciesLocked( String action, int reqPolicy, int userHandle) { if (isSeparateProfileChallengeEnabled(userHandle)) { sendAdminCommandLocked(action, reqPolicy, userHandle); } else { sendAdminCommandToSelfAndProfilesLocked(action, reqPolicy, userHandle); } } void removeActiveAdminLocked(final ComponentName adminReceiver, final int userHandle) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); DevicePolicyData policy = getUserData(userHandle); if (admin != null && !policy.mRemovingAdmins.contains(adminReceiver)) { policy.mRemovingAdmins.add(adminReceiver); sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { removeAdminArtifacts(adminReceiver, userHandle); removePackageIfRequired(adminReceiver.getPackageName(), userHandle); } }); } } public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle, boolean throwForMissiongPermission) { if (!mHasFeature) { return null; } enforceFullCrossUsersPermission(userHandle); ActivityInfo ai = null; try { ai = mIPackageManager.getReceiverInfo(adminName, PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); } catch (RemoteException e) { // shouldn't happen. } if (ai == null) { throw new IllegalArgumentException("Unknown admin: " + adminName); } if (!permission.BIND_DEVICE_ADMIN.equals(ai.permission)) { final String message = "DeviceAdminReceiver " + adminName + " must be protected with " + permission.BIND_DEVICE_ADMIN; Slog.w(LOG_TAG, message); if (throwForMissiongPermission && ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) { throw new IllegalArgumentException(message); } } try { return new DeviceAdminInfo(mContext, ai); } catch (XmlPullParserException | IOException e) { Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName, e); return null; } } private JournaledFile makeJournaledFile(int userHandle) { final String base = userHandle == UserHandle.USER_SYSTEM ? mInjector.getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML : new File(mInjector.environmentGetUserSystemDirectory(userHandle), DEVICE_POLICIES_XML).getAbsolutePath(); if (VERBOSE_LOG) { Log.v(LOG_TAG, "Opening " + base); } return new JournaledFile(new File(base), new File(base + ".tmp")); } private void saveSettingsLocked(int userHandle) { DevicePolicyData policy = getUserData(userHandle); JournaledFile journal = makeJournaledFile(userHandle); FileOutputStream stream = null; try { stream = new FileOutputStream(journal.chooseForWrite(), false); XmlSerializer out = new FastXmlSerializer(); out.setOutput(stream, StandardCharsets.UTF_8.name()); out.startDocument(null, true); out.startTag(null, "policies"); if (policy.mRestrictionsProvider != null) { out.attribute(null, ATTR_PERMISSION_PROVIDER, policy.mRestrictionsProvider.flattenToString()); } if (policy.mUserSetupComplete) { out.attribute(null, ATTR_SETUP_COMPLETE, Boolean.toString(true)); } if (policy.mDeviceProvisioningConfigApplied) { out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED, Boolean.toString(true)); } if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) { out.attribute(null, ATTR_PROVISIONING_STATE, Integer.toString(policy.mUserProvisioningState)); } if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) { out.attribute(null, ATTR_PERMISSION_POLICY, Integer.toString(policy.mPermissionPolicy)); } if (policy.mDelegatedCertInstallerPackage != null) { out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER, policy.mDelegatedCertInstallerPackage); } if (policy.mApplicationRestrictionsManagingPackage != null) { out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER, policy.mApplicationRestrictionsManagingPackage); } final int N = policy.mAdminList.size(); for (int i=0; i= 0) { out.startTag(null, "password-owner"); out.attribute(null, "value", Integer.toString(policy.mPasswordOwner)); out.endTag(null, "password-owner"); } if (policy.mFailedPasswordAttempts != 0) { out.startTag(null, "failed-password-attempts"); out.attribute(null, "value", Integer.toString(policy.mFailedPasswordAttempts)); out.endTag(null, "failed-password-attempts"); } if (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0 || policy.mActivePasswordUpperCase != 0 || policy.mActivePasswordLowerCase != 0 || policy.mActivePasswordLetters != 0 || policy.mActivePasswordNumeric != 0 || policy.mActivePasswordSymbols != 0 || policy.mActivePasswordNonLetter != 0) { out.startTag(null, "active-password"); out.attribute(null, "quality", Integer.toString(policy.mActivePasswordQuality)); out.attribute(null, "length", Integer.toString(policy.mActivePasswordLength)); out.attribute(null, "uppercase", Integer.toString(policy.mActivePasswordUpperCase)); out.attribute(null, "lowercase", Integer.toString(policy.mActivePasswordLowerCase)); out.attribute(null, "letters", Integer.toString(policy.mActivePasswordLetters)); out.attribute(null, "numeric", Integer .toString(policy.mActivePasswordNumeric)); out.attribute(null, "symbols", Integer.toString(policy.mActivePasswordSymbols)); out.attribute(null, "nonletter", Integer.toString(policy.mActivePasswordNonLetter)); out.endTag(null, "active-password"); } for (int i = 0; i < policy.mAcceptedCaCertificates.size(); i++) { out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES); out.attribute(null, ATTR_NAME, policy.mAcceptedCaCertificates.valueAt(i)); out.endTag(null, TAG_ACCEPTED_CA_CERTIFICATES); } for (int i=0; i outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } tag = parser.getName(); if ("admin".equals(tag)) { String name = parser.getAttributeValue(null, "name"); try { DeviceAdminInfo dai = findAdmin( ComponentName.unflattenFromString(name), userHandle, /* throwForMissionPermission= */ false); if (VERBOSE_LOG && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid) != userHandle)) { Slog.w(LOG_TAG, "findAdmin returned an incorrect uid " + dai.getActivityInfo().applicationInfo.uid + " for user " + userHandle); } if (dai != null) { ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false); ap.readFromXml(parser); policy.mAdminMap.put(ap.info.getComponent(), ap); } } catch (RuntimeException e) { Slog.w(LOG_TAG, "Failed loading admin " + name, e); } } else if ("failed-password-attempts".equals(tag)) { policy.mFailedPasswordAttempts = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("password-owner".equals(tag)) { policy.mPasswordOwner = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("active-password".equals(tag)) { policy.mActivePasswordQuality = Integer.parseInt( parser.getAttributeValue(null, "quality")); policy.mActivePasswordLength = Integer.parseInt( parser.getAttributeValue(null, "length")); policy.mActivePasswordUpperCase = Integer.parseInt( parser.getAttributeValue(null, "uppercase")); policy.mActivePasswordLowerCase = Integer.parseInt( parser.getAttributeValue(null, "lowercase")); policy.mActivePasswordLetters = Integer.parseInt( parser.getAttributeValue(null, "letters")); policy.mActivePasswordNumeric = Integer.parseInt( parser.getAttributeValue(null, "numeric")); policy.mActivePasswordSymbols = Integer.parseInt( parser.getAttributeValue(null, "symbols")); policy.mActivePasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, "nonletter")); } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) { policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) { policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name")); } else if (TAG_STATUS_BAR.equals(tag)) { policy.mStatusBarDisabled = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_DISABLED)); } else if (DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML.equals(tag)) { policy.doNotAskCredentialsOnBoot = true; } else if (TAG_AFFILIATION_ID.equals(tag)) { policy.mAffiliationIds.add(parser.getAttributeValue(null, "id")); } else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) { String pending = parser.getAttributeValue(null, ATTR_VALUE); policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending); } else if (TAG_INITIALIZATION_BUNDLE.equals(tag)) { policy.mInitBundle = PersistableBundle.restoreFromXml(parser); } else { Slog.w(LOG_TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); } } } catch (FileNotFoundException e) { // Don't be noisy, this is normal if we haven't defined any policies. } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException | IndexOutOfBoundsException e) { Slog.w(LOG_TAG, "failed parsing " + file, e); } try { if (stream != null) { stream.close(); } } catch (IOException e) { // Ignore } // Generate a list of admins from the admin map policy.mAdminList.addAll(policy.mAdminMap.values()); // Validate that what we stored for the password quality matches // sufficiently what is currently set. Note that this is only // a sanity check in case the two get out of sync; this should // never normally happen. final long identity = mInjector.binderClearCallingIdentity(); try { int actualPasswordQuality = mLockPatternUtils.getActivePasswordQuality(userHandle); if (actualPasswordQuality < policy.mActivePasswordQuality) { Slog.w(LOG_TAG, "Active password quality 0x" + Integer.toHexString(policy.mActivePasswordQuality) + " does not match actual quality 0x" + Integer.toHexString(actualPasswordQuality)); policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; policy.mActivePasswordLength = 0; policy.mActivePasswordUpperCase = 0; policy.mActivePasswordLowerCase = 0; policy.mActivePasswordLetters = 0; policy.mActivePasswordNumeric = 0; policy.mActivePasswordSymbols = 0; policy.mActivePasswordNonLetter = 0; } } finally { mInjector.binderRestoreCallingIdentity(identity); } validatePasswordOwnerLocked(policy); updateMaximumTimeToLockLocked(userHandle); updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle); if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); } } private void updateLockTaskPackagesLocked(List packages, int userId) { long ident = mInjector.binderClearCallingIdentity(); try { mInjector.getIActivityManager() .updateLockTaskPackages(userId, packages.toArray(new String[packages.size()])); } catch (RemoteException e) { // Not gonna happen. } finally { mInjector.binderRestoreCallingIdentity(ident); } } private void updateDeviceOwnerLocked() { long ident = mInjector.binderClearCallingIdentity(); try { // TODO This is to prevent DO from getting "clear data"ed, but it should also check the // user id and also protect all other DAs too. final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent(); if (deviceOwnerComponent != null) { mInjector.getIActivityManager() .updateDeviceOwner(deviceOwnerComponent.getPackageName()); } } catch (RemoteException e) { // Not gonna happen. } finally { mInjector.binderRestoreCallingIdentity(ident); } } static void validateQualityConstant(int quality) { switch (quality) { case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: return; } throw new IllegalArgumentException("Invalid quality constant: 0x" + Integer.toHexString(quality)); } void validatePasswordOwnerLocked(DevicePolicyData policy) { if (policy.mPasswordOwner >= 0) { boolean haveOwner = false; for (int i = policy.mAdminList.size() - 1; i >= 0; i--) { if (policy.mAdminList.get(i).getUid() == policy.mPasswordOwner) { haveOwner = true; break; } } if (!haveOwner) { Slog.w(LOG_TAG, "Previous password owner " + policy.mPasswordOwner + " no longer active; disabling"); policy.mPasswordOwner = -1; } } } @VisibleForTesting void systemReady(int phase) { if (!mHasFeature) { return; } switch (phase) { case SystemService.PHASE_LOCK_SETTINGS_READY: onLockSettingsReady(); break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. break; } } private void onLockSettingsReady() { getUserData(UserHandle.USER_SYSTEM); loadOwners(); cleanUpOldUsers(); onStartUser(UserHandle.USER_SYSTEM); // Register an observer for watching for user setup complete. new SetupContentObserver(mHandler).register(); // Initialize the user setup state, to handle the upgrade case. updateUserSetupComplete(); List packageList; synchronized (this) { packageList = getKeepUninstalledPackagesLocked(); } if (packageList != null) { mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList); } synchronized (this) { // push the force-ephemeral-users policy to the user manager. ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner != null) { mUserManagerInternal.setForceEphemeralUsers(deviceOwner.forceEphemeralUsers); } } } private void ensureDeviceOwnerUserStarted() { final int userId; synchronized (this) { if (!mOwners.hasDeviceOwner()) { return; } userId = mOwners.getDeviceOwnerUserId(); } if (VERBOSE_LOG) { Log.v(LOG_TAG, "Starting non-system DO user: " + userId); } if (userId != UserHandle.USER_SYSTEM) { try { mInjector.getIActivityManager().startUserInBackground(userId); // STOPSHIP Prevent the DO user from being killed. } catch (RemoteException e) { Slog.w(LOG_TAG, "Exception starting user", e); } } } private void onStartUser(int userId) { updateScreenCaptureDisabledInWindowManager(userId, getScreenCaptureDisabled(null, userId)); pushUserRestrictions(userId); } private void cleanUpOldUsers() { // This is needed in case the broadcast {@link Intent.ACTION_USER_REMOVED} was not handled // before reboot Set usersWithProfileOwners; Set usersWithData; synchronized(this) { usersWithProfileOwners = mOwners.getProfileOwnerKeys(); usersWithData = new ArraySet<>(); for (int i = 0; i < mUserData.size(); i++) { usersWithData.add(mUserData.keyAt(i)); } } List allUsers = mUserManager.getUsers(); Set deletedUsers = new ArraySet<>(); deletedUsers.addAll(usersWithProfileOwners); deletedUsers.addAll(usersWithData); for (UserInfo userInfo : allUsers) { deletedUsers.remove(userInfo.id); } for (Integer userId : deletedUsers) { removeUserData(userId); } } private void handlePasswordExpirationNotification(int userHandle) { synchronized (this) { final long now = System.currentTimeMillis(); List admins = getActiveAdminsForLockscreenPoliciesLocked( userHandle, /* parent */ false); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD) && admin.passwordExpirationTimeout > 0L && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS && admin.passwordExpirationDate > 0L) { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING); } } setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false); } } private class MonitoringCertNotificationTask extends AsyncTask { @Override protected Void doInBackground(Integer... params) { int userHandle = params[0]; if (userHandle == UserHandle.USER_ALL) { for (UserInfo userInfo : mUserManager.getUsers(true)) { manageNotification(userInfo.getUserHandle()); } } else { manageNotification(UserHandle.of(userHandle)); } return null; } private void manageNotification(UserHandle userHandle) { if (!mUserManager.isUserUnlocked(userHandle)) { return; } // Call out to KeyChain to check for CAs which are waiting for approval. final List pendingCertificates; try { pendingCertificates = getInstalledCaCertificates(userHandle); } catch (RemoteException | RuntimeException e) { Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e); return; } synchronized (DevicePolicyManagerService.this) { final DevicePolicyData policy = getUserData(userHandle.getIdentifier()); // Remove deleted certificates. Flush xml if necessary. if (policy.mAcceptedCaCertificates.retainAll(pendingCertificates)) { saveSettingsLocked(userHandle.getIdentifier()); } // Trim to approved certificates. pendingCertificates.removeAll(policy.mAcceptedCaCertificates); } if (pendingCertificates.isEmpty()) { mInjector.getNotificationManager().cancelAsUser( null, MONITORING_CERT_NOTIFICATION_ID, userHandle); return; } // Build and show a warning notification int smallIconId; String contentText; int parentUserId = userHandle.getIdentifier(); if (getProfileOwner(userHandle.getIdentifier()) != null) { contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed, getProfileOwnerName(userHandle.getIdentifier())); smallIconId = R.drawable.stat_sys_certificate_info; parentUserId = getProfileParentId(userHandle.getIdentifier()); } else if (getDeviceOwnerUserId() == userHandle.getIdentifier()) { contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed, getDeviceOwnerName()); smallIconId = R.drawable.stat_sys_certificate_info; } else { contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_unknown); smallIconId = android.R.drawable.stat_sys_warning; } final int numberOfCertificates = pendingCertificates.size(); Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO); dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); dialogIntent.setPackage("com.android.settings"); dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, numberOfCertificates); dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier()); PendingIntent notifyIntent = PendingIntent.getActivityAsUser(mContext, 0, dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null, new UserHandle(parentUserId)); final Context userContext; try { final String packageName = mContext.getPackageName(); userContext = mContext.createPackageContextAsUser(packageName, 0, userHandle); } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e); return; } final Notification noti = new Notification.Builder(userContext) .setSmallIcon(smallIconId) .setContentTitle(mContext.getResources().getQuantityText( R.plurals.ssl_ca_cert_warning, numberOfCertificates)) .setContentText(contentText) .setContentIntent(notifyIntent) .setPriority(Notification.PRIORITY_HIGH) .setShowWhen(false) .setColor(mContext.getColor( com.android.internal.R.color.system_notification_accent_color)) .build(); mInjector.getNotificationManager().notifyAsUser( null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); } private List getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { KeyChainConnection conn = null; try { conn = KeyChain.bindAsUser(mContext, userHandle); List aliases = conn.getService().getUserCaAliases().getList(); List result = new ArrayList<>(aliases.size()); for (int i = 0; i < aliases.size(); i++) { result.add(aliases.get(i).string); } return result; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } catch (AssertionError e) { throw new RuntimeException(e); } finally { if (conn != null) { conn.close(); } } } } /** * @param adminReceiver The admin to add * @param refreshing true = update an active admin, no error */ @Override public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle) { if (!mHasFeature) { return; } setActiveAdmin(adminReceiver, refreshing, userHandle, null); } private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle, Bundle onEnableData) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); enforceFullCrossUsersPermission(userHandle); DevicePolicyData policy = getUserData(userHandle); DeviceAdminInfo info = findAdmin(adminReceiver, userHandle, /* throwForMissionPermission= */ true); if (info == null) { throw new IllegalArgumentException("Bad admin: " + adminReceiver); } if (!info.getActivityInfo().applicationInfo.isInternal()) { throw new IllegalArgumentException("Only apps in internal storage can be active admin: " + adminReceiver); } synchronized (this) { long ident = mInjector.binderClearCallingIdentity(); try { final ActiveAdmin existingAdmin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (!refreshing && existingAdmin != null) { throw new IllegalArgumentException("Admin is already added"); } if (policy.mRemovingAdmins.contains(adminReceiver)) { throw new IllegalArgumentException( "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(); for (int i=0; i < N; i++) { ActiveAdmin oldAdmin = policy.mAdminList.get(i); if (oldAdmin.info.getComponent().equals(adminReceiver)) { replaceIndex = i; break; } } if (replaceIndex == -1) { policy.mAdminList.add(newAdmin); enableIfNecessary(info.getPackageName(), userHandle); } else { policy.mAdminList.set(replaceIndex, newAdmin); } saveSettingsLocked(userHandle); sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, onEnableData, null); } finally { mInjector.binderRestoreCallingIdentity(ident); } } } @Override public boolean isAdminActive(ComponentName adminReceiver, int userHandle) { if (!mHasFeature) { return false; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null; } } @Override public boolean isRemovingAdmin(ComponentName adminReceiver, int userHandle) { if (!mHasFeature) { return false; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { DevicePolicyData policyData = getUserData(userHandle); return policyData.mRemovingAdmins.contains(adminReceiver); } } @Override public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId, int userHandle) { if (!mHasFeature) { return false; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (administrator == null) { throw new SecurityException("No active admin " + adminReceiver); } return administrator.info.usesPolicy(policyId); } } @Override @SuppressWarnings("unchecked") public List getActiveAdmins(int userHandle) { if (!mHasFeature) { return Collections.EMPTY_LIST; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); if (N <= 0) { return null; } ArrayList res = new ArrayList(N); for (int i=0; i admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (mode < admin.passwordQuality) { mode = admin.passwordQuality; } } return mode; } } private List getActiveAdminsForLockscreenPoliciesLocked( int userHandle, boolean parent) { if (!parent && isSeparateProfileChallengeEnabled(userHandle)) { // If this user has a separate challenge, only return its restrictions. return getUserDataUnchecked(userHandle).mAdminList; } else { // Return all admins for this user and the profiles that are visible from this // user that do not use a separate work challenge. ArrayList admins = new ArrayList(); for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { DevicePolicyData policy = getUserData(userInfo.id); if (!userInfo.isManagedProfile()) { admins.addAll(policy.mAdminList); } else { // For managed profiles, we always include the policies set on the parent // profile. Additionally, we include the ones set on the managed profile // if no separate challenge is in place. boolean hasSeparateChallenge = isSeparateProfileChallengeEnabled(userInfo.id); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.hasParentActiveAdmin()) { admins.add(admin.getParentActiveAdmin()); } if (!hasSeparateChallenge) { admins.add(admin); } } } } return admins; } } private boolean isSeparateProfileChallengeEnabled(int userHandle) { long ident = mInjector.binderClearCallingIdentity(); try { return mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle); } finally { mInjector.binderRestoreCallingIdentity(ident); } } @Override public void setPasswordMinimumLength(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordLength != length) { ap.minimumPasswordLength = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumLength(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordLength : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (length < admin.minimumPasswordLength) { length = admin.minimumPasswordLength; } } return length; } } @Override public void setPasswordHistoryLength(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.passwordHistoryLength != length) { ap.passwordHistoryLength = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordHistoryLength(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.passwordHistoryLength : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (length < admin.passwordHistoryLength) { length = admin.passwordHistoryLength; } } return length; } } @Override public void setPasswordExpirationTimeout(ComponentName who, long timeout, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); Preconditions.checkArgumentNonnegative(timeout, "Timeout must be >= 0 ms"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD, parent); // Calling this API automatically bumps the expiration date final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L; ap.passwordExpirationDate = expiration; ap.passwordExpirationTimeout = timeout; if (timeout > 0L) { Slog.w(LOG_TAG, "setPasswordExpiration(): password will expire on " + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT) .format(new Date(expiration))); } saveSettingsLocked(userHandle); // in case this is the first one, set the alarm on the appropriate user. setExpirationAlarmCheckLocked(mContext, userHandle, parent); } } /** * Return a single admin's expiration cycle time, or the min of all cycle times. * Returns 0 if not configured. */ @Override public long getPasswordExpirationTimeout(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0L; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { long timeout = 0L; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.passwordExpirationTimeout : timeout; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (timeout == 0L || (admin.passwordExpirationTimeout != 0L && timeout > admin.passwordExpirationTimeout)) { timeout = admin.passwordExpirationTimeout; } } return timeout; } } @Override public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) { final int userId = UserHandle.getCallingUserId(); List changedProviders = null; synchronized (this) { ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (activeAdmin.crossProfileWidgetProviders == null) { activeAdmin.crossProfileWidgetProviders = new ArrayList<>(); } List providers = activeAdmin.crossProfileWidgetProviders; if (!providers.contains(packageName)) { providers.add(packageName); changedProviders = new ArrayList<>(providers); saveSettingsLocked(userId); } } if (changedProviders != null) { mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders); return true; } return false; } @Override public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) { final int userId = UserHandle.getCallingUserId(); List changedProviders = null; synchronized (this) { ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (activeAdmin.crossProfileWidgetProviders == null) { return false; } List providers = activeAdmin.crossProfileWidgetProviders; if (providers.remove(packageName)) { changedProviders = new ArrayList<>(providers); saveSettingsLocked(userId); } } if (changedProviders != null) { mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders); return true; } return false; } @Override public List getCrossProfileWidgetProviders(ComponentName admin) { synchronized (this) { ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (activeAdmin.crossProfileWidgetProviders == null || activeAdmin.crossProfileWidgetProviders.isEmpty()) { return null; } if (mInjector.binderIsCallingUidMyUid()) { return new ArrayList<>(activeAdmin.crossProfileWidgetProviders); } else { return activeAdmin.crossProfileWidgetProviders; } } } /** * Return a single admin's expiration date/time, or the min (soonest) for all admins. * Returns 0 if not configured. */ private long getPasswordExpirationLocked(ComponentName who, int userHandle, boolean parent) { long timeout = 0L; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.passwordExpirationDate : timeout; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (timeout == 0L || (admin.passwordExpirationDate != 0 && timeout > admin.passwordExpirationDate)) { timeout = admin.passwordExpirationDate; } } return timeout; } @Override public long getPasswordExpiration(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0L; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { return getPasswordExpirationLocked(who, userHandle, parent); } } @Override public void setPasswordMinimumUpperCase(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordUpperCase != length) { ap.minimumPasswordUpperCase = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumUpperCase(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordUpperCase : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (length < admin.minimumPasswordUpperCase) { length = admin.minimumPasswordUpperCase; } } return length; } } @Override public void setPasswordMinimumLowerCase(ComponentName who, int length, boolean parent) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordLowerCase != length) { ap.minimumPasswordLowerCase = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumLowerCase(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordLowerCase : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (length < admin.minimumPasswordLowerCase) { length = admin.minimumPasswordLowerCase; } } return length; } } @Override public void setPasswordMinimumLetters(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordLetters != length) { ap.minimumPasswordLetters = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumLetters(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordLetters : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) { continue; } if (length < admin.minimumPasswordLetters) { length = admin.minimumPasswordLetters; } } return length; } } @Override public void setPasswordMinimumNumeric(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordNumeric != length) { ap.minimumPasswordNumeric = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumNumeric(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordNumeric : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) { continue; } if (length < admin.minimumPasswordNumeric) { length = admin.minimumPasswordNumeric; } } return length; } } @Override public void setPasswordMinimumSymbols(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordSymbols != length) { ap.minimumPasswordSymbols = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumSymbols(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordSymbols : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) { continue; } if (length < admin.minimumPasswordSymbols) { length = admin.minimumPasswordSymbols; } } return length; } } @Override public void setPasswordMinimumNonLetter(ComponentName who, int length, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); if (ap.minimumPasswordNonLetter != length) { ap.minimumPasswordNonLetter = length; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getPasswordMinimumNonLetter(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.minimumPasswordNonLetter : length; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) { continue; } if (length < admin.minimumPasswordNonLetter) { length = admin.minimumPasswordNonLetter; } } return length; } } @Override public boolean isActivePasswordSufficient(int userHandle, boolean parent) { if (!mHasFeature) { return true; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent)); return isActivePasswordSufficientForUserLocked(policy, userHandle, parent); } } @Override public boolean isProfileActivePasswordSufficientForParent(int userHandle) { if (!mHasFeature) { return true; } enforceFullCrossUsersPermission(userHandle); enforceManagedProfile(userHandle, "call APIs refering to the parent profile"); synchronized (this) { int targetUser = getProfileParentId(userHandle); DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, false)); return isActivePasswordSufficientForUserLocked(policy, targetUser, false); } } private boolean isActivePasswordSufficientForUserLocked( DevicePolicyData policy, int userHandle, boolean parent) { 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 (requiredPasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { return true; } return policy.mActivePasswordUpperCase >= getPasswordMinimumUpperCase( null, userHandle, parent) && policy.mActivePasswordLowerCase >= getPasswordMinimumLowerCase( null, userHandle, parent) && policy.mActivePasswordLetters >= getPasswordMinimumLetters( null, userHandle, parent) && policy.mActivePasswordNumeric >= getPasswordMinimumNumeric( null, userHandle, parent) && policy.mActivePasswordSymbols >= getPasswordMinimumSymbols( null, userHandle, parent) && policy.mActivePasswordNonLetter >= getPasswordMinimumNonLetter( null, userHandle, parent); } @Override public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) { enforceFullCrossUsersPermission(userHandle); synchronized (this) { if (!isCallerWithSystemUid()) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked( null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent); } DevicePolicyData policy = getUserDataUnchecked(getCredentialOwner(userHandle, parent)); return policy.mFailedPasswordAttempts; } } @Override public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_WIPE_DATA, parent); ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent); if (ap.maximumFailedPasswordsForWipe != num) { ap.maximumFailedPasswordsForWipe = num; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public int getMaximumFailedPasswordsForWipe(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle, parent) : getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle, parent); return admin != null ? admin.maximumFailedPasswordsForWipe : 0; } } @Override public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent) { if (!mHasFeature) { return UserHandle.USER_NULL; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, parent); return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL; } } /** * Returns the admin with the strictest policy on maximum failed passwords for: *
    *
  • this user if it has a separate profile challenge, or *
  • this user and all profiles that don't have their own challenge otherwise. *
*

If the policy for the primary and any other profile are equal, it returns the admin for * the primary profile. * Returns {@code null} if no participating admin has that policy set. */ private ActiveAdmin getAdminWithMinimumFailedPasswordsForWipeLocked( int userHandle, boolean parent) { int count = 0; ActiveAdmin strictestAdmin = null; // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (admin.maximumFailedPasswordsForWipe == ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) { continue; // No max number of failed passwords policy set for this profile. } // We always favor the primary profile if several profiles have the same value set. int userId = admin.getUserHandle().getIdentifier(); if (count == 0 || count > admin.maximumFailedPasswordsForWipe || (count == admin.maximumFailedPasswordsForWipe && getUserInfo(userId).isPrimary())) { count = admin.maximumFailedPasswordsForWipe; strictestAdmin = admin; } } return strictestAdmin; } private UserInfo getUserInfo(@UserIdInt int userId) { final long token = mInjector.binderClearCallingIdentity(); try { return mUserManager.getUserInfo(userId); } finally { mInjector.binderRestoreCallingIdentity(token); } } @Override public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException { if (!mHasFeature) { return false; } final int callingUid = mInjector.binderGetCallingUid(); final int userHandle = mInjector.userHandleGetCallingUserId(); String password = passwordOrNull != null ? passwordOrNull : ""; // Password resetting to empty/null is not allowed for managed profiles. if (TextUtils.isEmpty(password)) { enforceNotManagedProfile(userHandle, "clear the active password"); } int quality; synchronized (this) { // If caller has PO (or DO) it can change the password, so see if that's the case first. ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked( null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid); final boolean preN; if (admin != null) { preN = getTargetSdk(admin.info.getPackageName(), userHandle) <= android.os.Build.VERSION_CODES.M; } else { // Otherwise, make sure the caller has any active admin with the right policy. admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); preN = getTargetSdk(admin.info.getPackageName(), userHandle) <= android.os.Build.VERSION_CODES.M; // As of N, password resetting to empty/null is not allowed anymore. // TODO Should we allow DO/PO to set an empty password? if (TextUtils.isEmpty(password)) { if (!preN) { throw new SecurityException("Cannot call with null password"); } else { Slog.e(LOG_TAG, "Cannot call with null password"); return false; } } // As of N, password cannot be changed by the admin if it is already set. if (isLockScreenSecureUnchecked(userHandle)) { if (!preN) { throw new SecurityException("Admin cannot change current password"); } else { Slog.e(LOG_TAG, "Admin cannot change current password"); return false; } } } // Do not allow to reset password when current user has a managed profile if (!isManagedProfile(userHandle)) { for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { if (userInfo.isManagedProfile()) { if (!preN) { throw new IllegalStateException( "Cannot reset password on user has managed profile"); } else { Slog.e(LOG_TAG, "Cannot reset password on user has managed profile"); return false; } } } } // Do not allow to reset password when user is locked if (!mUserManager.isUserUnlocked(userHandle)) { if (!preN) { throw new IllegalStateException("Cannot reset password when user is locked"); } else { Slog.e(LOG_TAG, "Cannot reset password when user is locked"); return false; } } quality = getPasswordQuality(null, userHandle, /* parent */ false); if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) { quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; } if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int realQuality = LockPatternUtils.computePasswordQuality(password); if (realQuality < quality && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { Slog.w(LOG_TAG, "resetPassword: password quality 0x" + Integer.toHexString(realQuality) + " does not meet required quality 0x" + Integer.toHexString(quality)); return false; } quality = Math.max(realQuality, quality); } int length = getPasswordMinimumLength(null, userHandle, /* parent */ false); if (password.length() < length) { Slog.w(LOG_TAG, "resetPassword: password length " + password.length() + " does not meet required length " + length); return false; } if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { int letters = 0; int uppercase = 0; int lowercase = 0; int numbers = 0; int symbols = 0; int nonletter = 0; for (int i = 0; i < password.length(); i++) { char c = password.charAt(i); if (c >= 'A' && c <= 'Z') { letters++; uppercase++; } else if (c >= 'a' && c <= 'z') { letters++; lowercase++; } else if (c >= '0' && c <= '9') { numbers++; nonletter++; } else { symbols++; nonletter++; } } int neededLetters = getPasswordMinimumLetters(null, userHandle, /* parent */ false); if(letters < neededLetters) { Slog.w(LOG_TAG, "resetPassword: number of letters " + letters + " does not meet required number of letters " + neededLetters); return false; } int neededNumbers = getPasswordMinimumNumeric(null, userHandle, /* parent */ false); if (numbers < neededNumbers) { Slog.w(LOG_TAG, "resetPassword: number of numerical digits " + numbers + " does not meet required number of numerical digits " + neededNumbers); return false; } int neededLowerCase = getPasswordMinimumLowerCase( null, userHandle, /* parent */ false); if (lowercase < neededLowerCase) { Slog.w(LOG_TAG, "resetPassword: number of lowercase letters " + lowercase + " does not meet required number of lowercase letters " + neededLowerCase); return false; } int neededUpperCase = getPasswordMinimumUpperCase( null, userHandle, /* parent */ false); if (uppercase < neededUpperCase) { Slog.w(LOG_TAG, "resetPassword: number of uppercase letters " + uppercase + " does not meet required number of uppercase letters " + neededUpperCase); return false; } int neededSymbols = getPasswordMinimumSymbols(null, userHandle, /* parent */ false); if (symbols < neededSymbols) { Slog.w(LOG_TAG, "resetPassword: number of special symbols " + symbols + " does not meet required number of special symbols " + neededSymbols); return false; } int neededNonLetter = getPasswordMinimumNonLetter( null, userHandle, /* parent */ false); if (nonletter < neededNonLetter) { Slog.w(LOG_TAG, "resetPassword: number of non-letter characters " + nonletter + " does not meet required number of non-letter characters " + neededNonLetter); return false; } } } DevicePolicyData policy = getUserData(userHandle); if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) { Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user"); return false; } boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid); boolean doNotAskCredentialsOnBoot = (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0; if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) { setDoNotAskCredentialsOnBoot(); } // Don't do this with the lock held, because it is going to call // back in to the service. final long ident = mInjector.binderClearCallingIdentity(); try { if (!TextUtils.isEmpty(password)) { mLockPatternUtils.saveLockPassword(password, null, quality, userHandle); } else { mLockPatternUtils.clearLock(userHandle); } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL); } synchronized (this) { int newOwner = requireEntry ? callingUid : -1; if (policy.mPasswordOwner != newOwner) { policy.mPasswordOwner = newOwner; saveSettingsLocked(userHandle); } } } finally { mInjector.binderRestoreCallingIdentity(ident); } return true; } private boolean isLockScreenSecureUnchecked(int userId) { long ident = mInjector.binderClearCallingIdentity(); try { return mLockPatternUtils.isSecure(userId); } finally { mInjector.binderRestoreCallingIdentity(ident); } } private void setDoNotAskCredentialsOnBoot() { synchronized (this) { DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM); if (!policyData.doNotAskCredentialsOnBoot) { policyData.doNotAskCredentialsOnBoot = true; saveSettingsLocked(UserHandle.USER_SYSTEM); } } } @Override public boolean getDoNotAskCredentialsOnBoot() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null); synchronized (this) { DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM); return policyData.doNotAskCredentialsOnBoot; } } @Override public void setMaximumTimeToLock(ComponentName who, long timeMs, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; saveSettingsLocked(userHandle); updateMaximumTimeToLockLocked(userHandle); } } } void updateMaximumTimeToLockLocked(int userHandle) { // Calculate the min timeout for all profiles - including the ones with a separate // challenge. Ideally if the timeout only affected the profile challenge we'd lock that // challenge only and keep the screen on. However there is no easy way of doing that at the // moment so we set the screen off timeout regardless of whether it affects the parent user // or the profile challenge only. long timeMs = Long.MAX_VALUE; int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle); for (int profileId : profileIds) { DevicePolicyData policy = getUserDataUnchecked(profileId); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.maximumTimeToUnlock > 0 && timeMs > admin.maximumTimeToUnlock) { timeMs = admin.maximumTimeToUnlock; } // If userInfo.id is a managed profile, we also need to look at // the policies set on the parent. if (admin.hasParentActiveAdmin()) { final ActiveAdmin parentAdmin = admin.getParentActiveAdmin(); if (parentAdmin.maximumTimeToUnlock > 0 && timeMs > parentAdmin.maximumTimeToUnlock) { timeMs = parentAdmin.maximumTimeToUnlock; } } } } // We only store the last maximum time to lock on the parent profile. So if calling from a // managed profile, retrieve the policy for the parent. DevicePolicyData policy = getUserDataUnchecked(getProfileParentId(userHandle)); if (policy.mLastMaximumTimeToLock == timeMs) { return; } policy.mLastMaximumTimeToLock = timeMs; final long ident = mInjector.binderClearCallingIdentity(); try { if (policy.mLastMaximumTimeToLock != Long.MAX_VALUE) { // Make sure KEEP_SCREEN_ON is disabled, since that // would allow bypassing of the maximum time to lock. mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } mInjector.getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin( (int) Math.min(policy.mLastMaximumTimeToLock, Integer.MAX_VALUE)); } finally { mInjector.binderRestoreCallingIdentity(ident); } } @Override public long getMaximumTimeToLock(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return admin != null ? admin.maximumTimeToUnlock : 0; } // Return the strictest policy across all participating admins. List admins = getActiveAdminsForLockscreenPoliciesLocked( userHandle, parent); return getMaximumTimeToLockPolicyFromAdmins(admins); } } @Override public long getMaximumTimeToLockForUserAndProfiles(int userHandle) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { // All admins for this user. ArrayList admins = new ArrayList(); for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) { DevicePolicyData policy = getUserData(userInfo.id); admins.addAll(policy.mAdminList); // If it is a managed profile, it may have parent active admins if (userInfo.isManagedProfile()) { for (ActiveAdmin admin : policy.mAdminList) { if (admin.hasParentActiveAdmin()) { admins.add(admin.getParentActiveAdmin()); } } } } return getMaximumTimeToLockPolicyFromAdmins(admins); } } private long getMaximumTimeToLockPolicyFromAdmins(List admins) { long time = 0; final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (time == 0) { time = admin.maximumTimeToUnlock; } else if (admin.maximumTimeToUnlock != 0 && time > admin.maximumTimeToUnlock) { time = admin.maximumTimeToUnlock; } } return time; } @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 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; } synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked( null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent); int userToLock = mInjector.userHandleGetCallingUserId(); // Unless this is a managed profile with work challenge enabled, lock all users. if (parent || !isSeparateProfileChallengeEnabled(userToLock)) { userToLock = UserHandle.USER_ALL; } final long ident = mInjector.binderClearCallingIdentity(); try { mLockPatternUtils.requireStrongAuth( STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, userToLock); if (userToLock == UserHandle.USER_ALL) { // Power off the display mInjector.powerManagerGoToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0); mInjector.getIWindowManager().lockNow(null); } else { mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true); } } catch (RemoteException e) { } finally { mInjector.binderRestoreCallingIdentity(ident); } } } @Override public void enforceCanManageCaCerts(ComponentName who) { if (who == null) { if (!isCallerDelegatedCertInstaller()) { mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); } } else { synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } } } private void enforceCanManageInstalledKeys(ComponentName who) { if (who == null) { if (!isCallerDelegatedCertInstaller()) { throw new SecurityException("who == null, but caller is not cert installer"); } } else { synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } } } private boolean isCallerDelegatedCertInstaller() { final int callingUid = mInjector.binderGetCallingUid(); final int userHandle = UserHandle.getUserId(callingUid); synchronized (this) { final DevicePolicyData policy = getUserData(userHandle); if (policy.mDelegatedCertInstallerPackage == null) { return false; } try { int uid = mContext.getPackageManager().getPackageUidAsUser( policy.mDelegatedCertInstallerPackage, userHandle); return uid == callingUid; } catch (NameNotFoundException e) { return false; } } } @Override public boolean approveCaCert(String alias, int userId, boolean approval) { enforceManageUsers(); synchronized (this) { Set certs = getUserData(userId).mAcceptedCaCertificates; boolean changed = (approval ? certs.add(alias) : certs.remove(alias)); if (!changed) { return false; } saveSettingsLocked(userId); } new MonitoringCertNotificationTask().execute(userId); return true; } @Override public boolean isCaCertApproved(String alias, int userId) { enforceManageUsers(); synchronized (this) { return getUserData(userId).mAcceptedCaCertificates.contains(alias); } } private void removeCaApprovalsIfNeeded(int userId) { for (UserInfo userInfo : mUserManager.getProfiles(userId)) { boolean isSecure = mLockPatternUtils.isSecure(userInfo.id); if (userInfo.isManagedProfile()){ isSecure |= mLockPatternUtils.isSecure(getProfileParentId(userInfo.id)); } if (!isSecure) { synchronized (this) { getUserData(userInfo.id).mAcceptedCaCertificates.clear(); saveSettingsLocked(userInfo.id); } new MonitoringCertNotificationTask().execute(userInfo.id); } } } @Override public boolean installCaCert(ComponentName admin, byte[] certBuffer) throws RemoteException { enforceCanManageCaCerts(admin); byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return false; } catch (IOException ioe) { Log.e(LOG_TAG, "Problem reading cert", ioe); return false; } final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); final long id = mInjector.binderClearCallingIdentity(); try { final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); try { keyChainConnection.getService().installCaCertificate(pemCert); return true; } catch (RemoteException e) { Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); } return false; } private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream( certBuffer)); } @Override public void uninstallCaCerts(ComponentName admin, String[] aliases) { enforceCanManageCaCerts(admin); final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); final long id = mInjector.binderClearCallingIdentity(); try { final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); try { for (int i = 0 ; i < aliases.length; i++) { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); } finally { keyChainConnection.close(); } } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); } } @Override public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, byte[] chain, String alias, boolean requestAccess) { enforceCanManageInstalledKeys(who); final int callingUid = mInjector.binderGetCallingUid(); final long id = mInjector.binderClearCallingIdentity(); try { final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid)); try { IKeyChainService keyChain = keyChainConnection.getService(); if (!keyChain.installKeyPair(privKey, cert, chain, alias)) { return false; } if (requestAccess) { keyChain.setGrant(callingUid, alias, true); } return true; } catch (RemoteException e) { Log.e(LOG_TAG, "Installing certificate", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e) { Log.w(LOG_TAG, "Interrupted while installing certificate", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); } return false; } @Override public boolean removeKeyPair(ComponentName who, String alias) { enforceCanManageInstalledKeys(who); final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); final long id = Binder.clearCallingIdentity(); try { final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); try { IKeyChainService keyChain = keyChainConnection.getService(); return keyChain.removeKeyPair(alias); } catch (RemoteException e) { Log.e(LOG_TAG, "Removing keypair", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e) { Log.w(LOG_TAG, "Interrupted while removing keypair", e); Thread.currentThread().interrupt(); } finally { Binder.restoreCallingIdentity(id); } return false; } @Override public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias, final IBinder response) { // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers. if (!isCallerWithSystemUid()) { return; } final UserHandle caller = mInjector.binderGetCallingUserHandle(); // If there is a profile owner, redirect to that; otherwise query the device owner. ComponentName aliasChooser = getProfileOwner(caller.getIdentifier()); if (aliasChooser == null && caller.isSystem()) { ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); if (deviceOwnerAdmin != null) { aliasChooser = deviceOwnerAdmin.info.getComponent(); } } if (aliasChooser == null) { sendPrivateKeyAliasResponse(null, response); return; } Intent intent = new Intent(DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS); intent.setComponent(aliasChooser); intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, uid); intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI, uri); intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias); intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final long id = mInjector.binderClearCallingIdentity(); try { mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String chosenAlias = getResultData(); sendPrivateKeyAliasResponse(chosenAlias, response); } }, null, Activity.RESULT_OK, null, null); } finally { mInjector.binderRestoreCallingIdentity(id); } } private void sendPrivateKeyAliasResponse(final String alias, final IBinder responseBinder) { final IKeyChainAliasCallback keyChainAliasResponse = IKeyChainAliasCallback.Stub.asInterface(responseBinder); new AsyncTask() { @Override protected Void doInBackground(Void... unused) { try { keyChainAliasResponse.alias(alias); } catch (Exception e) { // Catch everything (not just RemoteException): caller could throw a // RuntimeException back across processes. Log.e(LOG_TAG, "error while responding to callback", e); } return null; } }.execute(); } @Override public void setCertInstallerPackage(ComponentName who, String installerPackage) throws SecurityException { int userHandle = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) { if (installerPackage != null && !isPackageInstalledForUser(installerPackage, userHandle)) { throw new IllegalArgumentException("Package " + installerPackage + " is not installed on the current user"); } } DevicePolicyData policy = getUserData(userHandle); policy.mDelegatedCertInstallerPackage = installerPackage; saveSettingsLocked(userHandle); } } @Override public String getCertInstallerPackage(ComponentName who) throws SecurityException { int userHandle = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); DevicePolicyData policy = getUserData(userHandle); return policy.mDelegatedCertInstallerPackage; } } /** * @return {@code true} if the package is installed and set as always-on, {@code false} if it is * not installed and therefore not available. * * @throws SecurityException if the caller is not a profile or device owner. * @throws UnsupportedOperationException if the package does not support being set as always-on. */ @Override public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown) throws SecurityException { synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } final int userId = mInjector.userHandleGetCallingUserId(); final long token = mInjector.binderClearCallingIdentity(); try { if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) { return false; } ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdown)) { throw new UnsupportedOperationException(); } } finally { mInjector.binderRestoreCallingIdentity(token); } return true; } @Override public String getAlwaysOnVpnPackage(ComponentName admin) throws SecurityException { synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } final int userId = mInjector.userHandleGetCallingUserId(); final long token = mInjector.binderClearCallingIdentity(); try{ ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); return connectivityManager.getAlwaysOnVpnPackageForUser(userId); } finally { mInjector.binderRestoreCallingIdentity(token); } } private void wipeDataLocked(boolean wipeExtRequested, String reason) { if (wipeExtRequested) { StorageManager sm = (StorageManager) mContext.getSystemService( Context.STORAGE_SERVICE); sm.wipeAdoptableDisks(); } try { RecoverySystem.rebootWipeUserData(mContext, reason); } catch (IOException | SecurityException e) { Slog.w(LOG_TAG, "Failed requesting data wipe", e); } } @Override public void wipeData(int flags) { if (!mHasFeature) { return; } final int userHandle = mInjector.userHandleGetCallingUserId(); enforceFullCrossUsersPermission(userHandle); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. final ActiveAdmin admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); final String source = admin.info.getComponent().flattenToShortString(); long ident = mInjector.binderClearCallingIdentity(); try { if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { if (!isDeviceOwner(admin.info.getComponent(), userHandle)) { throw new SecurityException( "Only device owner admins can set WIPE_RESET_PROTECTION_DATA"); } PersistentDataBlockManager manager = (PersistentDataBlockManager) mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); if (manager != null) { manager.wipe(); } } boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0; wipeDeviceOrUserLocked(wipeExtRequested, userHandle, "DevicePolicyManager.wipeData() from " + source); } finally { mInjector.binderRestoreCallingIdentity(ident); } } } private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle, String reason) { if (userHandle == UserHandle.USER_SYSTEM) { wipeDataLocked(wipeExtRequested, reason); } else { mHandler.post(new Runnable() { @Override public void run() { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userHandle) { am.switchUser(UserHandle.USER_SYSTEM); } boolean isManagedProfile = isManagedProfile(userHandle); if (!mUserManager.removeUser(userHandle)) { Slog.w(LOG_TAG, "Couldn't remove user " + userHandle); } else if (isManagedProfile) { sendWipeProfileNotification(); } } catch (RemoteException re) { // Shouldn't happen } } }); } } private void sendWipeProfileNotification() { String contentText = mContext.getString(R.string.work_profile_deleted_description_dpm_wipe); Notification notification = new Notification.Builder(mContext) .setSmallIcon(android.R.drawable.stat_sys_warning) .setContentTitle(mContext.getString(R.string.work_profile_deleted)) .setContentText(contentText) .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setStyle(new Notification.BigTextStyle().bigText(contentText)) .build(); mInjector.getNotificationManager().notify(PROFILE_WIPED_NOTIFICATION_ID, notification); } private void clearWipeProfileNotification() { mInjector.getNotificationManager().cancel(PROFILE_WIPED_NOTIFICATION_ID); } @Override public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) { if (!mHasFeature) { return; } enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle); if (admin == null) { result.sendResult(null); return; } Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.setComponent(admin.info.getComponent()); mContext.sendOrderedBroadcastAsUser(intent, new UserHandle(userHandle), null, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { result.sendResult(getResultExtras(false)); } }, null, Activity.RESULT_OK, null, null); } } @Override public void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase, int numbers, int symbols, int nonletter, int userHandle) { if (!mHasFeature) { return; } enforceFullCrossUsersPermission(userHandle); // Managed Profile password can only be changed when it has a separate challenge. if (!isSeparateProfileChallengeEnabled(userHandle)) { enforceNotManagedProfile(userHandle, "set the active password"); } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); validateQualityConstant(quality); DevicePolicyData policy = getUserData(userHandle); long ident = mInjector.binderClearCallingIdentity(); try { synchronized (this) { policy.mActivePasswordQuality = quality; policy.mActivePasswordLength = length; policy.mActivePasswordLetters = letters; policy.mActivePasswordLowerCase = lowercase; policy.mActivePasswordUpperCase = uppercase; policy.mActivePasswordNumeric = numbers; policy.mActivePasswordSymbols = symbols; policy.mActivePasswordNonLetter = nonletter; policy.mFailedPasswordAttempts = 0; saveSettingsLocked(userHandle); updatePasswordExpirationsLocked(userHandle); setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false); // Send a broadcast to each profile using this password as its primary unlock. sendAdminCommandForLockscreenPoliciesLocked( DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); } removeCaApprovalsIfNeeded(userHandle); } finally { mInjector.binderRestoreCallingIdentity(ident); } } /** * Called any time the device password is updated. Resets all password expiration clocks. */ private void updatePasswordExpirationsLocked(int userHandle) { ArraySet affectedUserIds = new ArraySet(); List admins = getActiveAdminsForLockscreenPoliciesLocked( userHandle, /* parent */ false); final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) { affectedUserIds.add(admin.getUserHandle().getIdentifier()); long timeout = admin.passwordExpirationTimeout; long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L; admin.passwordExpirationDate = expiration; } } for (int affectedUserId : affectedUserIds) { saveSettingsLocked(affectedUserId); } } @Override public void reportFailedPasswordAttempt(int userHandle) { enforceFullCrossUsersPermission(userHandle); if (!isSeparateProfileChallengeEnabled(userHandle)) { enforceNotManagedProfile(userHandle, "report failed password attempt if separate profile challenge is not in place"); } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); final long ident = mInjector.binderClearCallingIdentity(); try { boolean wipeData = false; int identifier = 0; synchronized (this) { DevicePolicyData policy = getUserData(userHandle); policy.mFailedPasswordAttempts++; saveSettingsLocked(userHandle); if (mHasFeature) { ActiveAdmin strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, /* parent */ false); int max = strictestAdmin != null ? strictestAdmin.maximumFailedPasswordsForWipe : 0; if (max > 0 && policy.mFailedPasswordAttempts >= max) { // Wipe the user/profile associated with the policy that was violated. This // is not necessarily calling user: if the policy that fired was from a // managed profile rather than the main user profile, we wipe former only. wipeData = true; identifier = strictestAdmin.getUserHandle().getIdentifier(); } sendAdminCommandForLockscreenPoliciesLocked( DeviceAdminReceiver.ACTION_PASSWORD_FAILED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } } if (wipeData) { // Call without holding lock. wipeDeviceOrUserLocked(false, identifier, "reportFailedPasswordAttempt()"); } } finally { mInjector.binderRestoreCallingIdentity(ident); } if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 1); } } @Override public void reportSuccessfulPasswordAttempt(int userHandle) { enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); if (policy.mFailedPasswordAttempts != 0 || policy.mPasswordOwner >= 0) { long ident = mInjector.binderClearCallingIdentity(); try { policy.mFailedPasswordAttempts = 0; policy.mPasswordOwner = -1; saveSettingsLocked(userHandle); if (mHasFeature) { sendAdminCommandForLockscreenPoliciesLocked( DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } } finally { mInjector.binderRestoreCallingIdentity(ident); } } } if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1, /*method strength*/ 1); } } @Override public void reportFailedFingerprintAttempt(int userHandle) { enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 0); } } @Override public void reportSuccessfulFingerprintAttempt(int userHandle) { enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1, /*method strength*/ 0); } } @Override public void reportKeyguardDismissed(int userHandle) { enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED); } } @Override public void reportKeyguardSecured(int userHandle) { enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED); } } @Override public ComponentName setGlobalProxy(ComponentName who, String proxySpec, String exclusionList) { if (!mHasFeature) { return null; } synchronized(this) { Preconditions.checkNotNull(who, "ComponentName is null"); // Only check if system user has set global proxy. We don't allow other users to set it. DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); // Scan through active admins and find if anyone has already // set the global proxy. Set compSet = policy.mAdminMap.keySet(); for (ComponentName component : compSet) { ActiveAdmin ap = policy.mAdminMap.get(component); if ((ap.specifiesGlobalProxy) && (!component.equals(who))) { // Another admin already sets the global proxy // Return it to the caller. return component; } } // If the user is not system, don't set the global proxy. Fail silently. if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) { Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User " + UserHandle.getCallingUserId() + " is not permitted."); return null; } if (proxySpec == null) { admin.specifiesGlobalProxy = false; admin.globalProxySpec = null; admin.globalProxyExclusionList = null; } else { admin.specifiesGlobalProxy = true; admin.globalProxySpec = proxySpec; admin.globalProxyExclusionList = exclusionList; } // Reset the global proxy accordingly // Do this using system permissions, as apps cannot write to secure settings long origId = mInjector.binderClearCallingIdentity(); try { resetGlobalProxyLocked(policy); } finally { mInjector.binderRestoreCallingIdentity(origId); } return null; } } @Override public ComponentName getGlobalProxyAdmin(int userHandle) { if (!mHasFeature) { return null; } enforceFullCrossUsersPermission(userHandle); synchronized(this) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); // Scan through active admins and find if anyone has already // set the global proxy. final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin ap = policy.mAdminList.get(i); if (ap.specifiesGlobalProxy) { // Device admin sets the global proxy // Return it to the caller. return ap.info.getComponent(); } } } // No device admin sets the global proxy. return null; } @Override public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) { synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); } long token = mInjector.binderClearCallingIdentity(); try { ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); connectivityManager.setGlobalProxy(proxyInfo); } finally { mInjector.binderRestoreCallingIdentity(token); } } private void resetGlobalProxyLocked(DevicePolicyData policy) { final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin ap = policy.mAdminList.get(i); if (ap.specifiesGlobalProxy) { saveGlobalProxyLocked(ap.globalProxySpec, ap.globalProxyExclusionList); return; } } // No device admins defining global proxies - reset global proxy settings to none saveGlobalProxyLocked(null, null); } private void saveGlobalProxyLocked(String proxySpec, String exclusionList) { if (exclusionList == null) { exclusionList = ""; } if (proxySpec == null) { proxySpec = ""; } // Remove white spaces proxySpec = proxySpec.trim(); String data[] = proxySpec.split(":"); int proxyPort = 8080; if (data.length > 1) { try { proxyPort = Integer.parseInt(data[1]); } catch (NumberFormatException e) {} } exclusionList = exclusionList.trim(); ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList); if (!proxyProperties.isValid()) { Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString()); return; } mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]); mInjector.settingsGlobalPutInt(Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort); mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList); } /** * Set the storage encryption request for a single admin. Returns the new total request * status (for all admins). */ @Override public int setStorageEncryption(ComponentName who, boolean encrypt) { if (!mHasFeature) { return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = UserHandle.getCallingUserId(); synchronized (this) { // Check for permissions // Only system user can set storage encryption if (userHandle != UserHandle.USER_SYSTEM) { Slog.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. User " + UserHandle.getCallingUserId() + " is not permitted."); return 0; } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_ENCRYPTED_STORAGE); // Quick exit: If the filesystem does not support encryption, we can exit early. if (!isEncryptionSupported()) { return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } // (1) Record the value for the admin so it's sticky if (ap.encryptionRequested != encrypt) { ap.encryptionRequested = encrypt; saveSettingsLocked(userHandle); } DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); // (2) Compute "max" for all admins boolean newRequested = false; final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { newRequested |= policy.mAdminList.get(i).encryptionRequested; } // Notify OS of new request setEncryptionRequested(newRequested); // Return the new global request status return newRequested ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE : DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE; } } /** * Get the current storage encryption request status for a given admin, or aggregate of all * active admins. */ @Override public boolean getStorageEncryption(ComponentName who, int userHandle) { if (!mHasFeature) { return false; } enforceFullCrossUsersPermission(userHandle); synchronized (this) { // Check for permissions if a particular caller is specified if (who != null) { // When checking for a single caller, status is based on caller's request ActiveAdmin ap = getActiveAdminUncheckedLocked(who, userHandle); return ap != null ? ap.encryptionRequested : false; } // If no particular caller is specified, return the aggregate set of requests. // This is short circuited by returning true on the first hit. DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { if (policy.mAdminList.get(i).encryptionRequested) { return true; } } return false; } } /** * Get the current encryption status of the device. */ @Override public int getStorageEncryptionStatus(@Nullable String callerPackage, int userHandle) { if (!mHasFeature) { // Ok to return current status. } enforceFullCrossUsersPermission(userHandle); // It's not critical here, but let's make sure the package name is correct, in case // we start using it for different purposes. ensureCallerPackage(callerPackage); final ApplicationInfo ai; try { ai = mIPackageManager.getApplicationInfo(callerPackage, 0, userHandle); } catch (RemoteException e) { throw new SecurityException(e); } boolean legacyApp = false; if (ai.targetSdkVersion <= Build.VERSION_CODES.M) { legacyApp = true; } final int rawStatus = getEncryptionStatus(); if ((rawStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER) && legacyApp) { return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE; } return rawStatus; } /** * Hook to low-levels: This should report if the filesystem supports encrypted storage. */ private boolean isEncryptionSupported() { // Note, this can be implemented as // return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; // But is provided as a separate internal method if there's a faster way to do a // simple check for supported-or-not. return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } /** * Hook to low-levels: Reporting the current status of encryption. * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED}, * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE}, * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_PER_USER}, or * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}. */ private int getEncryptionStatus() { if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) { return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER; } else if (mInjector.storageManagerIsNonDefaultBlockEncrypted()) { return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE; } else if (mInjector.storageManagerIsEncrypted()) { return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY; } else if (mInjector.storageManagerIsEncryptable()) { return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE; } else { return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } } /** * Hook to low-levels: If needed, record the new admin setting for encryption. */ private void setEncryptionRequested(boolean encrypt) { } /** * Set whether the screen capture is disabled for the user managed by the specified admin. */ @Override public void setScreenCaptureDisabled(ComponentName who, boolean disabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = UserHandle.getCallingUserId(); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (ap.disableScreenCapture != disabled) { ap.disableScreenCapture = disabled; saveSettingsLocked(userHandle); updateScreenCaptureDisabledInWindowManager(userHandle, disabled); } } } /** * Returns whether or not screen capture is disabled for a given admin, or disabled for any * active admin (if given admin is null). */ @Override public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) { if (!mHasFeature) { return false; } synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return (admin != null) ? admin.disableScreenCapture : false; } DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.disableScreenCapture) { return true; } } return false; } } private void updateScreenCaptureDisabledInWindowManager(final int userHandle, final boolean disabled) { mHandler.post(new Runnable() { @Override public void run() { try { mInjector.getIWindowManager().setScreenCaptureDisabled(userHandle, disabled); } catch (RemoteException e) { Log.w(LOG_TAG, "Unable to notify WindowManager.", e); } } }); } /** * Set whether auto time is required by the specified admin (must be device owner). */ @Override public void setAutoTimeRequired(ComponentName who, boolean required) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = UserHandle.getCallingUserId(); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); if (admin.requireAutoTime != required) { admin.requireAutoTime = required; saveSettingsLocked(userHandle); } } // Turn AUTO_TIME on in settings if it is required if (required) { long ident = mInjector.binderClearCallingIdentity(); try { mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */); } finally { mInjector.binderRestoreCallingIdentity(ident); } } } /** * Returns whether or not auto time is required by the device owner. */ @Override public boolean getAutoTimeRequired() { if (!mHasFeature) { return false; } synchronized (this) { ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); return (deviceOwner != null) ? deviceOwner.requireAutoTime : false; } } @Override public void setForceEphemeralUsers(ComponentName who, boolean forceEphemeralUsers) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); // Allow setting this policy to true only if there is a split system user. if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) { throw new UnsupportedOperationException( "Cannot force ephemeral users on systems without split system user."); } boolean removeAllUsers = false; synchronized (this) { final ActiveAdmin deviceOwner = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) { deviceOwner.forceEphemeralUsers = forceEphemeralUsers; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers); removeAllUsers = forceEphemeralUsers; } } if (removeAllUsers) { long identitity = mInjector.binderClearCallingIdentity(); try { mUserManagerInternal.removeAllUsers(); } finally { mInjector.binderRestoreCallingIdentity(identitity); } } } @Override public boolean getForceEphemeralUsers(ComponentName who) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { final ActiveAdmin deviceOwner = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); return deviceOwner.forceEphemeralUsers; } } private boolean isDeviceOwnerManagedSingleUserDevice() { synchronized (this) { if (!mOwners.hasDeviceOwner()) { return false; } } final long callingIdentity = mInjector.binderClearCallingIdentity(); try { if (mInjector.userManagerIsSplitSystemUser()) { // In split system user mode, only allow the case where the device owner is managing // the only non-system user of the device return (mUserManager.getUserCount() == 2 && mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM); } else { return mUserManager.getUserCount() == 1; } } finally { mInjector.binderRestoreCallingIdentity(callingIdentity); } } private void ensureDeviceOwnerManagingSingleUser(ComponentName who) throws SecurityException { synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); } if (!isDeviceOwnerManagedSingleUserDevice()) { throw new SecurityException( "There should only be one user, managed by Device Owner"); } } @Override public boolean requestBugreport(ComponentName who) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); ensureDeviceOwnerManagingSingleUser(who); if (mRemoteBugreportServiceIsActive.get() || (getDeviceOwnerRemoteBugreportUri() != null)) { Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running."); return false; } final long callingIdentity = mInjector.binderClearCallingIdentity(); try { ActivityManagerNative.getDefault().requestBugReport( ActivityManager.BUGREPORT_OPTION_REMOTE); mRemoteBugreportServiceIsActive.set(true); mRemoteBugreportSharingAccepted.set(false); registerRemoteBugreportReceivers(); mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID, RemoteBugreportUtils.buildNotification(mContext, DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL); mHandler.postDelayed(mRemoteBugreportTimeoutRunnable, RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS); return true; } catch (RemoteException re) { // should never happen Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re); return false; } finally { mInjector.binderRestoreCallingIdentity(callingIdentity); } } synchronized void sendDeviceOwnerCommand(String action, Bundle extras) { Intent intent = new Intent(action); intent.setComponent(mOwners.getDeviceOwnerComponent()); if (extras != null) { intent.putExtras(extras); } mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId())); } private synchronized String getDeviceOwnerRemoteBugreportUri() { return mOwners.getDeviceOwnerRemoteBugreportUri(); } private synchronized void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri, String bugreportHash) { mOwners.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUri, bugreportHash); } private void registerRemoteBugreportReceivers() { try { IntentFilter filterFinished = new IntentFilter( DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH, RemoteBugreportUtils.BUGREPORT_MIMETYPE); mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished); } catch (IntentFilter.MalformedMimeTypeException e) { // should never happen, as setting a constant Slog.w(LOG_TAG, "Failed to set type " + RemoteBugreportUtils.BUGREPORT_MIMETYPE, e); } IntentFilter filterConsent = new IntentFilter(); filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED); filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED); mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent); } private void onBugreportFinished(Intent intent) { mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable); mRemoteBugreportServiceIsActive.set(false); Uri bugreportUri = intent.getData(); String bugreportUriString = null; if (bugreportUri != null) { bugreportUriString = bugreportUri.toString(); } String bugreportHash = intent.getStringExtra( DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH); if (mRemoteBugreportSharingAccepted.get()) { shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash); mInjector.getNotificationManager().cancel(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID); } else { setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash); mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID, RemoteBugreportUtils.buildNotification(mContext, DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED), UserHandle.ALL); } mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver); } private void onBugreportFailed() { mRemoteBugreportServiceIsActive.set(false); mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP, RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE); mRemoteBugreportSharingAccepted.set(false); setDeviceOwnerRemoteBugreportUriAndHash(null, null); mInjector.getNotificationManager().cancel(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID); Bundle extras = new Bundle(); extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON, DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING); sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras); mContext.unregisterReceiver(mRemoteBugreportConsentReceiver); mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver); } private void onBugreportSharingAccepted() { mRemoteBugreportSharingAccepted.set(true); String bugreportUriString = null; String bugreportHash = null; synchronized (this) { bugreportUriString = getDeviceOwnerRemoteBugreportUri(); bugreportHash = mOwners.getDeviceOwnerRemoteBugreportHash(); } if (bugreportUriString != null) { shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash); } else if (mRemoteBugreportServiceIsActive.get()) { mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID, RemoteBugreportUtils.buildNotification(mContext, DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED), UserHandle.ALL); } } private void onBugreportSharingDeclined() { if (mRemoteBugreportServiceIsActive.get()) { mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP, RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE); mRemoteBugreportServiceIsActive.set(false); mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable); mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver); } mRemoteBugreportSharingAccepted.set(false); setDeviceOwnerRemoteBugreportUriAndHash(null, null); sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null); } private void shareBugreportWithDeviceOwnerIfExists(String bugreportUriString, String bugreportHash) { ParcelFileDescriptor pfd = null; try { if (bugreportUriString == null) { throw new FileNotFoundException(); } Uri bugreportUri = Uri.parse(bugreportUriString); pfd = mContext.getContentResolver().openFileDescriptor(bugreportUri, "r"); synchronized (this) { Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE); 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); mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId())); } } catch (FileNotFoundException e) { Bundle extras = new Bundle(); extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON, DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE); sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras); } finally { try { if (pfd != null) { pfd.close(); } } catch (IOException ex) { // Ignore } mRemoteBugreportSharingAccepted.set(false); setDeviceOwnerRemoteBugreportUriAndHash(null, null); } } /** * Disables all device cameras according to the specified admin. */ @Override public void setCameraDisabled(ComponentName who, boolean disabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); if (ap.disableCamera != disabled) { ap.disableCamera = disabled; saveSettingsLocked(userHandle); } } // Tell the user manager that the restrictions have changed. pushUserRestrictions(userHandle); } /** * Gets whether or not all device cameras are disabled for a given admin, or disabled for any * active admins. */ @Override public boolean getCameraDisabled(ComponentName who, int userHandle) { return getCameraDisabled(who, userHandle, /* mergeDeviceOwnerRestriction= */ true); } private boolean getCameraDisabled(ComponentName who, int userHandle, boolean mergeDeviceOwnerRestriction) { if (!mHasFeature) { return false; } synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return (admin != null) ? admin.disableCamera : false; } // First, see if DO has set it. If so, it's device-wide. if (mergeDeviceOwnerRestriction) { final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner != null && deviceOwner.disableCamera) { return true; } } // Then check each device admin on the user. DevicePolicyData policy = getUserData(userHandle); // Determine whether or not the device camera is disabled for any active admins. final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.disableCamera) { return true; } } return false; } } @Override public void setKeyguardDisabledFeatures(ComponentName who, int which, boolean parent) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); if (isManagedProfile(userHandle)) { if (parent) { which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; } else { which = which & PROFILE_KEYGUARD_FEATURES; } } synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent); if (ap.disabledKeyguardFeatures != which) { ap.disabledKeyguardFeatures = which; saveSettingsLocked(userHandle); } } } /** * Gets the disabled state for features in keyguard for the given admin, * or the aggregate of all active admins if who is null. */ @Override public int getKeyguardDisabledFeatures(ComponentName who, int userHandle, boolean parent) { if (!mHasFeature) { return 0; } enforceFullCrossUsersPermission(userHandle); final long ident = mInjector.binderClearCallingIdentity(); try { synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return (admin != null) ? admin.disabledKeyguardFeatures : 0; } final List admins; if (!parent && isManagedProfile(userHandle)) { // If we are being asked about a managed profile, just return keyguard features // disabled by admins in the profile. admins = getUserDataUnchecked(userHandle).mAdminList; } else { // Otherwise return those set by admins in the user and its profiles. admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); } int which = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; final int N = admins.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = admins.get(i); int userId = admin.getUserHandle().getIdentifier(); boolean isRequestedUser = !parent && (userId == userHandle); if (isRequestedUser || !isManagedProfile(userId)) { // If we are being asked explicitly about this user // return all disabled features even if its a managed profile. which |= admin.disabledKeyguardFeatures; } else { // Otherwise a managed profile is only allowed to disable // some features on the parent user. which |= (admin.disabledKeyguardFeatures & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER); } } return which; } } finally { mInjector.binderRestoreCallingIdentity(ident); } } @Override public void setKeepUninstalledPackages(ComponentName who, List packageList) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); Preconditions.checkNotNull(packageList, "packageList is null"); final int userHandle = UserHandle.getCallingUserId(); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); admin.keepUninstalledPackages = packageList; saveSettingsLocked(userHandle); mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList); } } @Override public List getKeepUninstalledPackages(ComponentName who) { Preconditions.checkNotNull(who, "ComponentName is null"); if (!mHasFeature) { return null; } // TODO In split system user mode, allow apps on user 0 to query the list synchronized (this) { // Check if this is the device owner who is calling getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); return getKeepUninstalledPackagesLocked(); } } private List getKeepUninstalledPackagesLocked() { ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); return (deviceOwner != null) ? deviceOwner.keepUninstalledPackages : null; } @Override public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) { if (!mHasFeature) { return false; } if (admin == null || !isPackageInstalledForUser(admin.getPackageName(), userId)) { throw new IllegalArgumentException("Invalid component " + admin + " for device owner"); } synchronized (this) { enforceCanSetDeviceOwnerLocked(admin, userId); if (getActiveAdminUncheckedLocked(admin, userId) == null || getUserData(userId).mRemovingAdmins.contains(admin)) { throw new IllegalArgumentException("Not active admin: " + admin); } // Shutting down backup manager service permanently. long ident = mInjector.binderClearCallingIdentity(); try { if (mInjector.getIBackupManager() != null) { mInjector.getIBackupManager() .setBackupServiceActive(UserHandle.USER_SYSTEM, false); } } catch (RemoteException e) { throw new IllegalStateException("Failed deactivating backup service.", e); } finally { mInjector.binderRestoreCallingIdentity(ident); } mOwners.setDeviceOwner(admin, ownerName, userId); mOwners.writeDeviceOwner(); updateDeviceOwnerLocked(); setDeviceOwnerSystemPropertyLocked(); Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED); ident = mInjector.binderClearCallingIdentity(); try { // TODO Send to system too? mContext.sendBroadcastAsUser(intent, new UserHandle(userId)); } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); return true; } } public boolean isDeviceOwner(ComponentName who, int userId) { synchronized (this) { return mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userId && mOwners.getDeviceOwnerComponent().equals(who); } } public boolean isProfileOwner(ComponentName who, int userId) { final ComponentName profileOwner = getProfileOwner(userId); return who != null && who.equals(profileOwner); } @Override public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) { if (!mHasFeature) { return null; } if (!callingUserOnly) { enforceManageUsers(); } synchronized (this) { if (!mOwners.hasDeviceOwner()) { return null; } if (callingUserOnly && mInjector.userHandleGetCallingUserId() != mOwners.getDeviceOwnerUserId()) { return null; } return mOwners.getDeviceOwnerComponent(); } } @Override public int getDeviceOwnerUserId() { if (!mHasFeature) { return UserHandle.USER_NULL; } enforceManageUsers(); synchronized (this) { return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL; } } /** * Returns the "name" of the device owner. It'll work for non-DO users too, but requires * MANAGE_USERS. */ @Override public String getDeviceOwnerName() { if (!mHasFeature) { return null; } enforceManageUsers(); synchronized (this) { if (!mOwners.hasDeviceOwner()) { return null; } // TODO This totally ignores the name passed to setDeviceOwner (change for b/20679292) // Should setDeviceOwner/ProfileOwner still take a name? String deviceOwnerPackage = mOwners.getDeviceOwnerPackageName(); return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_SYSTEM); } } // Returns the active device owner or null if there is no device owner. @VisibleForTesting ActiveAdmin getDeviceOwnerAdminLocked() { ComponentName component = mOwners.getDeviceOwnerComponent(); if (component == null) { return null; } DevicePolicyData policy = getUserData(mOwners.getDeviceOwnerUserId()); final int n = policy.mAdminList.size(); for (int i = 0; i < n; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (component.equals(admin.info.getComponent())) { return admin; } } Slog.wtf(LOG_TAG, "Active admin for device owner not found. component=" + component); return null; } @Override public void clearDeviceOwner(String packageName) { Preconditions.checkNotNull(packageName, "packageName is null"); final int callingUid = mInjector.binderGetCallingUid(); try { int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, UserHandle.getUserId(callingUid)); if (uid != callingUid) { throw new SecurityException("Invalid packageName"); } } catch (NameNotFoundException e) { throw new SecurityException(e); } synchronized (this) { final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent(); final int deviceOwnerUserId = mOwners.getDeviceOwnerUserId(); if (!mOwners.hasDeviceOwner() || !deviceOwnerComponent.getPackageName().equals(packageName) || (deviceOwnerUserId != UserHandle.getUserId(callingUid))) { throw new SecurityException( "clearDeviceOwner can only be called by the device owner"); } enforceUserUnlocked(deviceOwnerUserId); final ActiveAdmin admin = getDeviceOwnerAdminLocked(); long ident = mInjector.binderClearCallingIdentity(); try { clearDeviceOwnerLocked(admin, deviceOwnerUserId); removeActiveAdminLocked(deviceOwnerComponent, deviceOwnerUserId); } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent); } } private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) { if (admin != null) { admin.disableCamera = false; admin.userRestrictions = null; admin.forceEphemeralUsers = false; mUserManagerInternal.setForceEphemeralUsers(admin.forceEphemeralUsers); } clearUserPoliciesLocked(userId); mOwners.clearDeviceOwner(); mOwners.writeDeviceOwner(); updateDeviceOwnerLocked(); disableDeviceOwnerManagedSingleUserFeaturesIfNeeded(); try { // Reactivate backup service. mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true); } catch (RemoteException e) { throw new IllegalStateException("Failed reactivating backup service.", e); } } @Override public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) { if (!mHasFeature) { return false; } if (who == null || !isPackageInstalledForUser(who.getPackageName(), userHandle)) { throw new IllegalArgumentException("Component " + who + " not installed for userId:" + userHandle); } synchronized (this) { enforceCanSetProfileOwnerLocked(who, userHandle); if (getActiveAdminUncheckedLocked(who, userHandle) == null || getUserData(userHandle).mRemovingAdmins.contains(who)) { throw new IllegalArgumentException("Not active admin: " + who); } mOwners.setProfileOwner(who, ownerName, userHandle); mOwners.writeProfileOwner(userHandle); Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle); return true; } } @Override public void clearProfileOwner(ComponentName who) { if (!mHasFeature) { return; } final UserHandle callingUser = mInjector.binderGetCallingUserHandle(); final int userId = callingUser.getIdentifier(); enforceNotManagedProfile(userId, "clear profile owner"); enforceUserUnlocked(userId); // Check if this is the profile owner who is calling final ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); synchronized (this) { final long ident = mInjector.binderClearCallingIdentity(); try { clearProfileOwnerLocked(admin, userId); removeActiveAdminLocked(who, userId); } finally { mInjector.binderRestoreCallingIdentity(ident); } Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId); } } public void clearProfileOwnerLocked(ActiveAdmin admin, int userId) { if (admin != null) { admin.disableCamera = false; admin.userRestrictions = null; } clearUserPoliciesLocked(userId); mOwners.removeProfileOwner(userId); mOwners.writeProfileOwner(userId); } @Override public void setDeviceOwnerLockScreenInfo(ComponentName who, CharSequence info) { Preconditions.checkNotNull(who, "ComponentName is null"); if (!mHasFeature) { return; } synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); long token = mInjector.binderClearCallingIdentity(); try { mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null); } finally { mInjector.binderRestoreCallingIdentity(token); } } } @Override public CharSequence getDeviceOwnerLockScreenInfo() { return mLockPatternUtils.getDeviceOwnerInfo(); } private void clearUserPoliciesLocked(int userId) { // Reset some of the user-specific policies DevicePolicyData policy = getUserData(userId); policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT; policy.mDelegatedCertInstallerPackage = null; policy.mApplicationRestrictionsManagingPackage = null; policy.mStatusBarDisabled = false; policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED; saveSettingsLocked(userId); try { mIPackageManager.updatePermissionFlagsForAllApps( PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0 /* flagValues */, userId); pushUserRestrictions(userId); } catch (RemoteException re) { // Shouldn't happen. } } @Override public boolean hasUserSetupCompleted() { return hasUserSetupCompleted(UserHandle.getCallingUserId()); } private boolean hasUserSetupCompleted(int userHandle) { if (!mHasFeature) { return true; } return getUserData(userHandle).mUserSetupComplete; } @Override public int getUserProvisioningState() { if (!mHasFeature) { return DevicePolicyManager.STATE_USER_UNMANAGED; } int userHandle = mInjector.userHandleGetCallingUserId(); return getUserProvisioningState(userHandle); } private int getUserProvisioningState(int userHandle) { return getUserData(userHandle).mUserProvisioningState; } @Override public void setUserProvisioningState(int newState, int userHandle) { if (!mHasFeature) { return; } if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle) && getManagedUserId(userHandle) == -1) { // No managed device, user or profile, so setting provisioning state makes no sense. throw new IllegalStateException("Not allowed to change provisioning state unless a " + "device or profile owner is set."); } synchronized (this) { boolean transitionCheckNeeded = true; // Calling identity/permission checks. final int callingUid = mInjector.binderGetCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { // ADB shell can only move directly from un-managed to finalized as part of directly // setting profile-owner or device-owner. if (getUserProvisioningState(userHandle) != DevicePolicyManager.STATE_USER_UNMANAGED || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) { throw new IllegalStateException("Not allowed to change provisioning state " + "unless current provisioning state is unmanaged, and new state is " + "finalized."); } transitionCheckNeeded = false; } else { // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS. enforceCanManageProfileAndDeviceOwners(); } final DevicePolicyData policyData = getUserData(userHandle); if (transitionCheckNeeded) { // Optional state transition check for non-ADB case. checkUserProvisioningStateTransition(policyData.mUserProvisioningState, newState); } policyData.mUserProvisioningState = newState; saveSettingsLocked(userHandle); } } private void checkUserProvisioningStateTransition(int currentState, int newState) { // Valid transitions for normal use-cases. switch (currentState) { case DevicePolicyManager.STATE_USER_UNMANAGED: // Can move to any state from unmanaged (except itself as an edge case).. if (newState != DevicePolicyManager.STATE_USER_UNMANAGED) { return; } break; case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE: case DevicePolicyManager.STATE_USER_SETUP_COMPLETE: // Can only move to finalized from these states. if (newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED) { return; } break; case DevicePolicyManager.STATE_USER_PROFILE_COMPLETE: // Current user has a managed-profile, but current user is not managed, so // rather than moving to finalized state, go back to unmanaged once // profile provisioning is complete. if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) { return; } break; case DevicePolicyManager.STATE_USER_SETUP_FINALIZED: // Cannot transition out of finalized. break; } // Didn't meet any of the accepted state transition checks above, throw appropriate error. throw new IllegalStateException("Cannot move to user provisioning state [" + newState + "] " + "from state [" + currentState + "]"); } @Override public void setProfileEnabled(ComponentName who) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { // Check if this is the profile owner who is calling getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); final int userId = UserHandle.getCallingUserId(); enforceManagedProfile(userId, "enable the profile"); long id = mInjector.binderClearCallingIdentity(); try { mUserManager.setUserEnabled(userId); UserInfo parent = mUserManager.getProfileParent(userId); Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED); intent.putExtra(Intent.EXTRA_USER, new UserHandle(userId)); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id)); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public void setProfileName(ComponentName who, String profileName) { Preconditions.checkNotNull(who, "ComponentName is null"); int userId = UserHandle.getCallingUserId(); // Check if this is the profile owner (includes device owner). getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { mUserManager.setUserName(userId, profileName); } finally { mInjector.binderRestoreCallingIdentity(id); } } @Override public ComponentName getProfileOwner(int userHandle) { if (!mHasFeature) { return null; } synchronized (this) { return mOwners.getProfileOwnerComponent(userHandle); } } // Returns the active profile owner for this user or null if the current user has no // profile owner. @VisibleForTesting ActiveAdmin getProfileOwnerAdminLocked(int userHandle) { ComponentName profileOwner = mOwners.getProfileOwnerComponent(userHandle); if (profileOwner == null) { return null; } DevicePolicyData policy = getUserData(userHandle); final int n = policy.mAdminList.size(); for (int i = 0; i < n; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (profileOwner.equals(admin.info.getComponent())) { return admin; } } return null; } @Override public String getProfileOwnerName(int userHandle) { if (!mHasFeature) { return null; } enforceManageUsers(); ComponentName profileOwner = getProfileOwner(userHandle); if (profileOwner == null) { return null; } return getApplicationLabel(profileOwner.getPackageName(), userHandle); } /** * Canonical name for a given package. */ private String getApplicationLabel(String packageName, int userHandle) { long token = mInjector.binderClearCallingIdentity(); try { final Context userContext; try { UserHandle handle = new UserHandle(userHandle); userContext = mContext.createPackageContextAsUser(packageName, 0, handle); } catch (PackageManager.NameNotFoundException nnfe) { Log.w(LOG_TAG, packageName + " is not installed for user " + userHandle, nnfe); return null; } ApplicationInfo appInfo = userContext.getApplicationInfo(); CharSequence result = null; if (appInfo != null) { PackageManager pm = userContext.getPackageManager(); result = pm.getApplicationLabel(appInfo); } return result != null ? result.toString() : null; } finally { mInjector.binderRestoreCallingIdentity(token); } } /** * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS * permission. * The profile owner can only be set before the user setup phase has completed, * except for: * - SYSTEM_UID * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked}) */ private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) { UserInfo info = getUserInfo(userHandle); if (info == null) { // User doesn't exist. throw new IllegalArgumentException( "Attempted to set profile owner for invalid userId: " + userHandle); } if (info.isGuest()) { throw new IllegalStateException("Cannot set a profile owner on a guest"); } if (mOwners.hasProfileOwner(userHandle)) { throw new IllegalStateException("Trying to set the profile owner, but profile owner " + "is already set."); } if (mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userHandle) { throw new IllegalStateException("Trying to set the profile owner, but the user " + "already has a device owner."); } int callingUid = mInjector.binderGetCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { if (hasUserSetupCompleted(userHandle) && hasIncompatibleAccountsLocked(userHandle, owner)) { 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()) { 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(@Nullable ComponentName owner, int userId) { int callingUid = mInjector.binderGetCallingUid(); boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; if (!isAdb) { enforceCanManageProfileAndDeviceOwners(); } final int code = checkSetDeviceOwnerPreConditionLocked(owner, userId, isAdb); switch (code) { case CODE_OK: return; case CODE_HAS_DEVICE_OWNER: throw new IllegalStateException( "Trying to set the device owner, but device owner is already set."); case CODE_USER_HAS_PROFILE_OWNER: throw new IllegalStateException("Trying to set the device owner, but the user " + "already has a profile owner."); case CODE_USER_NOT_RUNNING: throw new IllegalStateException("User not running: " + userId); case CODE_NOT_SYSTEM_USER: throw new IllegalStateException("User is not system user"); case CODE_USER_SETUP_COMPLETED: throw new IllegalStateException( "Cannot set the device owner if the device is already set-up"); case CODE_NONSYSTEM_USER_EXISTS: throw new IllegalStateException("Not allowed to set the device owner because there " + "are already several users on the device"); case CODE_ACCOUNTS_NOT_EMPTY: throw new IllegalStateException("Not allowed to set the device owner because there " + "are already some accounts on the device"); default: throw new IllegalStateException("Unknown @DeviceOwnerPreConditionCode " + code); } } private void enforceUserUnlocked(int userId) { // Since we're doing this operation on behalf of an app, we only // want to use the actual "unlocked" state. Preconditions.checkState(mUserManager.isUserUnlocked(userId), "User must be running and unlocked"); } private void enforceManageUsers() { final int callingUid = mInjector.binderGetCallingUid(); if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); } } private void enforceFullCrossUsersPermission(int userHandle) { enforceSystemUserOrPermission(userHandle, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); } private void enforceCrossUsersPermission(int userHandle) { enforceSystemUserOrPermission(userHandle, android.Manifest.permission.INTERACT_ACROSS_USERS); } private void enforceSystemUserOrPermission(int userHandle, String permission) { if (userHandle < 0) { throw new IllegalArgumentException("Invalid userId " + userHandle); } final int callingUid = mInjector.binderGetCallingUid(); if (userHandle == UserHandle.getUserId(callingUid)) { return; } if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) { mContext.enforceCallingOrSelfPermission(permission, "Must be system or have " + permission + " permission"); } } private void enforceManagedProfile(int userHandle, String message) { if(!isManagedProfile(userHandle)) { throw new SecurityException("You can not " + message + " outside a managed profile."); } } private void enforceNotManagedProfile(int userHandle, String message) { if(isManagedProfile(userHandle)) { throw new SecurityException("You can not " + message + " for a managed profile."); } } private void ensureCallerPackage(@Nullable String packageName) { if (packageName == null) { Preconditions.checkState(isCallerWithSystemUid(), "Only caller can omit package name"); } else { final int callingUid = mInjector.binderGetCallingUid(); final int userId = mInjector.userHandleGetCallingUserId(); try { final ApplicationInfo ai = mIPackageManager.getApplicationInfo( packageName, 0, userId); Preconditions.checkState(ai.uid == callingUid, "Unmatching package name"); } catch (RemoteException e) { // Shouldn't happen } } } private boolean isCallerWithSystemUid() { return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID); } private int getProfileParentId(int userHandle) { final long ident = mInjector.binderClearCallingIdentity(); try { UserInfo parentUser = mUserManager.getProfileParent(userHandle); return parentUser != null ? parentUser.id : userHandle; } finally { mInjector.binderRestoreCallingIdentity(ident); } } private int getCredentialOwner(int userHandle, boolean parent) { final long ident = mInjector.binderClearCallingIdentity(); try { if (parent) { UserInfo parentProfile = mUserManager.getProfileParent(userHandle); if (parentProfile != null) { userHandle = parentProfile.id; } } return mUserManager.getCredentialOwnerProfile(userHandle); } finally { mInjector.binderRestoreCallingIdentity(ident); } } private boolean isManagedProfile(int userHandle) { return getUserInfo(userHandle).isManagedProfile(); } private void enableIfNecessary(String packageName, int userId) { try { ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName, PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId); if (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { mIPackageManager.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager"); } } catch (RemoteException e) { } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid=" + mInjector.binderGetCallingPid() + ", uid=" + mInjector.binderGetCallingUid()); return; } synchronized (this) { pw.println("Current Device Policy Manager state:"); mOwners.dump(" ", pw); int userCount = mUserData.size(); for (int u = 0; u < userCount; u++) { DevicePolicyData policy = getUserData(mUserData.keyAt(u)); pw.println(); pw.println(" Enabled Device Admins (User " + policy.mUserHandle + ", provisioningState: " + policy.mUserProvisioningState + "):"); final int N = policy.mAdminList.size(); for (int i=0; i getTrustAgentConfiguration(ComponentName admin, ComponentName agent, int userHandle, boolean parent) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(agent, "agent null"); enforceFullCrossUsersPermission(userHandle); synchronized (this) { final String componentName = agent.flattenToString(); if (admin != null) { final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle, parent); if (ap == null) return null; TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName); if (trustAgentInfo == null || trustAgentInfo.options == null) return null; List result = new ArrayList<>(); result.add(trustAgentInfo.options); return result; } // Return strictest policy for this user and profiles that are visible from this user. List result = null; // Search through all admins that use KEYGUARD_DISABLE_TRUST_AGENTS and keep track // of the options. If any admin doesn't have options, discard options for the rest // and return null. List admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent); boolean allAdminsHaveOptions = true; final int N = admins.size(); for (int i = 0; i < N; i++) { final ActiveAdmin active = admins.get(i); final boolean disablesTrust = (active.disabledKeyguardFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0; final TrustAgentInfo info = active.trustAgentInfos.get(componentName); if (info != null && info.options != null && !info.options.isEmpty()) { if (disablesTrust) { if (result == null) { result = new ArrayList<>(); } result.add(info.options); } else { Log.w(LOG_TAG, "Ignoring admin " + active.info + " because it has trust options but doesn't declare " + "KEYGUARD_DISABLE_TRUST_AGENTS"); } } else if (disablesTrust) { allAdminsHaveOptions = false; break; } } return allAdminsHaveOptions ? result : null; } } @Override public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); int userHandle = UserHandle.getCallingUserId(); DevicePolicyData userData = getUserData(userHandle); userData.mRestrictionsProvider = permissionProvider; saveSettingsLocked(userHandle); } } @Override public ComponentName getRestrictionsProvider(int userHandle) { synchronized (this) { if (!isCallerWithSystemUid()) { throw new SecurityException("Only the system can query the permission provider"); } DevicePolicyData userData = getUserData(userHandle); return userData != null ? userData.mRestrictionsProvider : null; } } @Override public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { UserInfo parent = mUserManager.getProfileParent(callingUserId); if (parent == null) { Slog.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no " + "parent"); return; } if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) { mIPackageManager.addCrossProfileIntentFilter( filter, who.getPackageName(), callingUserId, parent.id, 0); } if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) { mIPackageManager.addCrossProfileIntentFilter(filter, who.getPackageName(), parent.id, callingUserId, 0); } } catch (RemoteException re) { // Shouldn't happen } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public void clearCrossProfileIntentFilters(ComponentName who) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { UserInfo parent = mUserManager.getProfileParent(callingUserId); if (parent == null) { Slog.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no " + "parent"); return; } // Removing those that go from the managed profile to the parent. mIPackageManager.clearCrossProfileIntentFilters( callingUserId, who.getPackageName()); // And those that go from the parent to the managed profile. // If we want to support multiple managed profiles, we will have to only remove // those that have callingUserId as their target. mIPackageManager.clearCrossProfileIntentFilters(parent.id, who.getPackageName()); } catch (RemoteException re) { // Shouldn't happen } finally { mInjector.binderRestoreCallingIdentity(id); } } } /** * @return true if all packages in enabledPackages are either in the list * permittedList or are a system app. */ private boolean checkPackagesInPermittedListOrSystem(List enabledPackages, List permittedList, int userIdToCheck) { long id = mInjector.binderClearCallingIdentity(); try { // If we have an enabled packages list for a managed profile the packages // we should check are installed for the parent user. UserInfo user = getUserInfo(userIdToCheck); if (user.isManagedProfile()) { userIdToCheck = user.profileGroupId; } for (String enabledPackage : enabledPackages) { boolean systemService = false; try { ApplicationInfo applicationInfo = mIPackageManager.getApplicationInfo( enabledPackage, PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck); systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } catch (RemoteException e) { Log.i(LOG_TAG, "Can't talk to package managed", e); } if (!systemService && !permittedList.contains(enabledPackage)) { return false; } } } finally { mInjector.binderRestoreCallingIdentity(id); } return true; } private AccessibilityManager getAccessibilityManagerForUser(int userId) { // Not using AccessibilityManager.getInstance because that guesses // at the user you require based on callingUid and caches for a given // process. IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); IAccessibilityManager service = iBinder == null ? null : IAccessibilityManager.Stub.asInterface(iBinder); return new AccessibilityManager(mContext, service, userId); } @Override public boolean setPermittedAccessibilityServices(ComponentName who, List packageList) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); if (packageList != null) { int userId = UserHandle.getCallingUserId(); List enabledServices = null; long id = mInjector.binderClearCallingIdentity(); try { UserInfo user = getUserInfo(userId); if (user.isManagedProfile()) { userId = user.profileGroupId; } AccessibilityManager accessibilityManager = getAccessibilityManagerForUser(userId); enabledServices = accessibilityManager.getEnabledAccessibilityServiceList( AccessibilityServiceInfo.FEEDBACK_ALL_MASK); } finally { mInjector.binderRestoreCallingIdentity(id); } if (enabledServices != null) { List enabledPackages = new ArrayList(); for (AccessibilityServiceInfo service : enabledServices) { enabledPackages.add(service.getResolveInfo().serviceInfo.packageName); } if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList, userId)) { Slog.e(LOG_TAG, "Cannot set permitted accessibility services, " + "because it contains already enabled accesibility services."); return false; } } } synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); admin.permittedAccessiblityServices = packageList; saveSettingsLocked(UserHandle.getCallingUserId()); } return true; } @Override public List getPermittedAccessibilityServices(ComponentName who) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.permittedAccessiblityServices; } } @Override public List getPermittedAccessibilityServicesForUser(int userId) { if (!mHasFeature) { return null; } synchronized (this) { List result = null; // If we have multiple profiles we return the intersection of the // permitted lists. This can happen in cases where we have a device // and profile owner. int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId); for (int profileId : profileIds) { // Just loop though all admins, only device or profiles // owners can have permitted lists set. DevicePolicyData policy = getUserDataUnchecked(profileId); final int N = policy.mAdminList.size(); for (int j = 0; j < N; j++) { ActiveAdmin admin = policy.mAdminList.get(j); List fromAdmin = admin.permittedAccessiblityServices; if (fromAdmin != null) { if (result == null) { result = new ArrayList<>(fromAdmin); } else { result.retainAll(fromAdmin); } } } } // If we have a permitted list add all system accessibility services. if (result != null) { long id = mInjector.binderClearCallingIdentity(); try { UserInfo user = getUserInfo(userId); if (user.isManagedProfile()) { userId = user.profileGroupId; } AccessibilityManager accessibilityManager = getAccessibilityManagerForUser(userId); List installedServices = accessibilityManager.getInstalledAccessibilityServiceList(); if (installedServices != null) { for (AccessibilityServiceInfo service : installedServices) { ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; ApplicationInfo applicationInfo = serviceInfo.applicationInfo; if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { result.add(serviceInfo.packageName); } } } } finally { mInjector.binderRestoreCallingIdentity(id); } } return result; } } @Override public boolean isAccessibilityServicePermittedByAdmin(ComponentName who, String packageName, int userHandle) { if (!mHasFeature) { return true; } Preconditions.checkNotNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(packageName, "packageName is null"); if (!isCallerWithSystemUid()){ throw new SecurityException( "Only the system can query if an accessibility service is disabled by admin"); } synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin == null) { return false; } if (admin.permittedAccessiblityServices == null) { return true; } return checkPackagesInPermittedListOrSystem(Arrays.asList(packageName), admin.permittedAccessiblityServices, userHandle); } } private boolean checkCallerIsCurrentUserOrProfile() { int callingUserId = UserHandle.getCallingUserId(); long token = mInjector.binderClearCallingIdentity(); try { UserInfo currentUser; UserInfo callingUser = getUserInfo(callingUserId); try { currentUser = mInjector.getIActivityManager().getCurrentUser(); } catch (RemoteException e) { Slog.e(LOG_TAG, "Failed to talk to activity managed.", e); return false; } if (callingUser.isManagedProfile() && callingUser.profileGroupId != currentUser.id) { Slog.e(LOG_TAG, "Cannot set permitted input methods for managed profile " + "of a user that isn't the foreground user."); return false; } if (!callingUser.isManagedProfile() && callingUserId != currentUser.id ) { Slog.e(LOG_TAG, "Cannot set permitted input methods " + "of a user that isn't the foreground user."); return false; } } finally { mInjector.binderRestoreCallingIdentity(token); } return true; } @Override public boolean setPermittedInputMethods(ComponentName who, List packageList) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); // TODO When InputMethodManager supports per user calls remove // this restriction. if (!checkCallerIsCurrentUserOrProfile()) { return false; } if (packageList != null) { // InputMethodManager fetches input methods for current user. // So this can only be set when calling user is the current user // or parent is current user in case of managed profiles. InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class); List enabledImes = inputMethodManager.getEnabledInputMethodList(); if (enabledImes != null) { List enabledPackages = new ArrayList(); for (InputMethodInfo ime : enabledImes) { enabledPackages.add(ime.getPackageName()); } if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList, mInjector.binderGetCallingUserHandle().getIdentifier())) { Slog.e(LOG_TAG, "Cannot set permitted input methods, " + "because it contains already enabled input method."); return false; } } } synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); admin.permittedInputMethods = packageList; saveSettingsLocked(UserHandle.getCallingUserId()); } return true; } @Override public List getPermittedInputMethods(ComponentName who) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.permittedInputMethods; } } @Override public List getPermittedInputMethodsForCurrentUser() { UserInfo currentUser; try { currentUser = mInjector.getIActivityManager().getCurrentUser(); } catch (RemoteException e) { Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e); // Activity managed is dead, just allow all IMEs return null; } int userId = currentUser.id; synchronized (this) { List result = null; // If we have multiple profiles we return the intersection of the // permitted lists. This can happen in cases where we have a device // and profile owner. int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId); for (int profileId : profileIds) { // Just loop though all admins, only device or profiles // owners can have permitted lists set. DevicePolicyData policy = getUserDataUnchecked(profileId); final int N = policy.mAdminList.size(); for (int j = 0; j < N; j++) { ActiveAdmin admin = policy.mAdminList.get(j); List fromAdmin = admin.permittedInputMethods; if (fromAdmin != null) { if (result == null) { result = new ArrayList(fromAdmin); } else { result.retainAll(fromAdmin); } } } } // If we have a permitted list add all system input methods. if (result != null) { InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class); List imes = inputMethodManager.getInputMethodList(); long id = mInjector.binderClearCallingIdentity(); try { if (imes != null) { for (InputMethodInfo ime : imes) { ServiceInfo serviceInfo = ime.getServiceInfo(); ApplicationInfo applicationInfo = serviceInfo.applicationInfo; if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { result.add(serviceInfo.packageName); } } } } finally { mInjector.binderRestoreCallingIdentity(id); } } return result; } } @Override public boolean isInputMethodPermittedByAdmin(ComponentName who, String packageName, int userHandle) { if (!mHasFeature) { return true; } Preconditions.checkNotNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(packageName, "packageName is null"); if (!isCallerWithSystemUid()) { throw new SecurityException( "Only the system can query if an input method is disabled by admin"); } synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin == null) { return false; } if (admin.permittedInputMethods == null) { return true; } return checkPackagesInPermittedListOrSystem(Arrays.asList(packageName), admin.permittedInputMethods, userHandle); } } private void sendAdminEnabledBroadcastLocked(int userHandle) { DevicePolicyData policyData = getUserData(userHandle); if (policyData.mAdminBroadcastPending) { // Send the initialization data to profile owner and delete the data ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle); if (admin != null) { PersistableBundle initBundle = policyData.mInitBundle; sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, initBundle == null ? null : new Bundle(initBundle), null); } policyData.mInitBundle = null; policyData.mAdminBroadcastPending = false; saveSettingsLocked(userHandle); } } @Override public UserHandle createAndManageUser(ComponentName admin, String name, ComponentName profileOwner, PersistableBundle adminExtras, int flags) { Preconditions.checkNotNull(admin, "admin is null"); Preconditions.checkNotNull(profileOwner, "profileOwner is null"); if (!admin.getPackageName().equals(profileOwner.getPackageName())) { throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin " + admin + " are not in the same package"); } // Only allow the system user to use this method if (!mInjector.binderGetCallingUserHandle().isSystem()) { throw new SecurityException("createAndManageUser was called from non-system user"); } if (!mInjector.userManagerIsSplitSystemUser() && (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) { throw new IllegalArgumentException( "Ephemeral users are only supported on systems with a split system user."); } // Create user. UserHandle user = null; synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); final long id = mInjector.binderClearCallingIdentity(); try { int userInfoFlags = 0; if ((flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) { userInfoFlags |= UserInfo.FLAG_EPHEMERAL; } UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name, userInfoFlags); if (userInfo != null) { user = userInfo.getUserHandle(); } } finally { mInjector.binderRestoreCallingIdentity(id); } } if (user == null) { return null; } // Set admin. final long id = mInjector.binderClearCallingIdentity(); try { final String adminPkg = admin.getPackageName(); final int userHandle = user.getIdentifier(); try { // Install the profile owner if not present. if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) { mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle); } } catch (RemoteException e) { Slog.e(LOG_TAG, "Failed to make remote calls for createAndManageUser, " + "removing created user", e); mUserManager.removeUser(user.getIdentifier()); return null; } setActiveAdmin(profileOwner, true, userHandle); // User is not started yet, the broadcast by setActiveAdmin will not be received. // So we store adminExtras for broadcasting when the user starts for first time. synchronized(this) { DevicePolicyData policyData = getUserData(userHandle); policyData.mInitBundle = adminExtras; policyData.mAdminBroadcastPending = true; saveSettingsLocked(userHandle); } final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier()); setProfileOwner(profileOwner, ownerName, userHandle); if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle); } return user; } finally { mInjector.binderRestoreCallingIdentity(id); } } @Override public boolean removeUser(ComponentName who, UserHandle userHandle) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { return mUserManager.removeUser(userHandle.getIdentifier()); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public boolean switchUser(ComponentName who, UserHandle userHandle) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { int userId = UserHandle.USER_SYSTEM; if (userHandle != null) { userId = userHandle.getIdentifier(); } return mInjector.getIActivityManager().switchUser(userId); } catch (RemoteException e) { Log.e(LOG_TAG, "Couldn't switch user", e); return false; } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public Bundle getApplicationRestrictions(ComponentName who, String packageName) { enforceCanManageApplicationRestrictions(who); final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); try { Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle); // if no restrictions were saved, mUserManager.getApplicationRestrictions // returns null, but DPM method should return an empty Bundle as per JavaDoc return bundle != null ? bundle : Bundle.EMPTY; } finally { mInjector.binderRestoreCallingIdentity(id); } } @Override public String[] setPackagesSuspended(ComponentName who, String[] packageNames, boolean suspended) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { return mIPackageManager.setPackagesSuspendedAsUser( packageNames, suspended, callingUserId); } catch (RemoteException re) { // Shouldn't happen. Slog.e(LOG_TAG, "Failed talking to the package manager", re); } finally { mInjector.binderRestoreCallingIdentity(id); } return packageNames; } } @Override public boolean isPackageSuspended(ComponentName who, String packageName) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { return mIPackageManager.isPackageSuspendedForUser(packageName, callingUserId); } catch (RemoteException re) { // Shouldn't happen. Slog.e(LOG_TAG, "Failed talking to the package manager", re); } finally { mInjector.binderRestoreCallingIdentity(id); } return false; } } @Override public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) { Preconditions.checkNotNull(who, "ComponentName is null"); if (!UserRestrictionsUtils.isValidRestriction(key)) { return; } final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); final boolean isDeviceOwner = isDeviceOwner(who, userHandle); if (isDeviceOwner) { if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) { throw new SecurityException("Device owner cannot set user restriction " + key); } } else { // profile owner if (!UserRestrictionsUtils.canProfileOwnerChange(key, userHandle)) { throw new SecurityException("Profile owner cannot set user restriction " + key); } } // Save the restriction to ActiveAdmin. activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner); saveSettingsLocked(userHandle); pushUserRestrictions(userHandle); sendChangedNotification(userHandle); } } private void pushUserRestrictions(int userId) { synchronized (this) { final Bundle global; final Bundle local = new Bundle(); if (mOwners.isDeviceOwnerUserId(userId)) { global = new Bundle(); final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner == null) { return; // Shouldn't happen. } UserRestrictionsUtils.sortToGlobalAndLocal(deviceOwner.userRestrictions, global, local); // DO can disable camera globally. if (deviceOwner.disableCamera) { global.putBoolean(UserManager.DISALLOW_CAMERA, true); } } else { global = null; ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); if (profileOwner != null) { UserRestrictionsUtils.merge(local, profileOwner.userRestrictions); } } // Also merge in *local* camera restriction. if (getCameraDisabled(/* who= */ null, userId, /* mergeDeviceOwnerRestriction= */ false)) { local.putBoolean(UserManager.DISALLOW_CAMERA, true); } mUserManagerInternal.setDevicePolicyUserRestrictions(userId, local, global); } } @Override public Bundle getUserRestrictions(ComponentName who) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { final ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return activeAdmin.userRestrictions; } } @Override public boolean setApplicationHidden(ComponentName who, String packageName, boolean hidden) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { return mIPackageManager.setApplicationHiddenSettingAsUser( packageName, hidden, callingUserId); } catch (RemoteException re) { // shouldn't happen Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re); } finally { mInjector.binderRestoreCallingIdentity(id); } return false; } } @Override public boolean isApplicationHidden(ComponentName who, String packageName) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { return mIPackageManager.getApplicationHiddenSettingAsUser( packageName, callingUserId); } catch (RemoteException re) { // shouldn't happen Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re); } finally { mInjector.binderRestoreCallingIdentity(id); } return false; } } @Override public void enableSystemApp(ComponentName who, String packageName) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); int userId = UserHandle.getCallingUserId(); long id = mInjector.binderClearCallingIdentity(); try { if (VERBOSE_LOG) { Slog.v(LOG_TAG, "installing " + packageName + " for " + userId); } int parentUserId = getProfileParentId(userId); if (!isSystemApp(mIPackageManager, packageName, parentUserId)) { throw new IllegalArgumentException("Only system apps can be enabled this way."); } // Install the app. mIPackageManager.installExistingPackageAsUser(packageName, userId); } catch (RemoteException re) { // shouldn't happen Slog.wtf(LOG_TAG, "Failed to install " + packageName, re); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public int enableSystemAppWithIntent(ComponentName who, Intent intent) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); int userId = UserHandle.getCallingUserId(); long id = mInjector.binderClearCallingIdentity(); try { int parentUserId = getProfileParentId(userId); List activitiesToEnable = mIPackageManager .queryIntentActivities(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, parentUserId) .getList(); if (VERBOSE_LOG) { Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable); } int numberOfAppsInstalled = 0; if (activitiesToEnable != null) { for (ResolveInfo info : activitiesToEnable) { if (info.activityInfo != null) { String packageName = info.activityInfo.packageName; if (isSystemApp(mIPackageManager, packageName, parentUserId)) { numberOfAppsInstalled++; mIPackageManager.installExistingPackageAsUser(packageName, userId); } else { Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a" + " system app"); } } } } return numberOfAppsInstalled; } catch (RemoteException e) { // shouldn't happen Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent); return 0; } finally { mInjector.binderRestoreCallingIdentity(id); } } } private boolean isSystemApp(IPackageManager pm, String packageName, int userId) throws RemoteException { ApplicationInfo appInfo = pm.getApplicationInfo(packageName, GET_UNINSTALLED_PACKAGES, userId); if (appInfo == null) { throw new IllegalArgumentException("The application " + packageName + " is not present on this device"); } return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } @Override public void setAccountManagementDisabled(ComponentName who, String accountType, boolean disabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (disabled) { ap.accountTypesWithManagementDisabled.add(accountType); } else { ap.accountTypesWithManagementDisabled.remove(accountType); } saveSettingsLocked(UserHandle.getCallingUserId()); } } @Override public String[] getAccountTypesWithManagementDisabled() { return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId()); } @Override public String[] getAccountTypesWithManagementDisabledAsUser(int userId) { enforceFullCrossUsersPermission(userId); if (!mHasFeature) { return null; } synchronized (this) { DevicePolicyData policy = getUserData(userId); final int N = policy.mAdminList.size(); ArraySet resultSet = new ArraySet<>(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); resultSet.addAll(admin.accountTypesWithManagementDisabled); } return resultSet.toArray(new String[resultSet.size()]); } } @Override public void setUninstallBlocked(ComponentName who, String packageName, boolean uninstallBlocked) { Preconditions.checkNotNull(who, "ComponentName is null"); final int userId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { mIPackageManager.setBlockUninstallForUser(packageName, uninstallBlocked, userId); } catch (RemoteException re) { // Shouldn't happen. Slog.e(LOG_TAG, "Failed to setBlockUninstallForUser", re); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public boolean isUninstallBlocked(ComponentName who, String packageName) { // This function should return true if and only if the package is blocked by // setUninstallBlocked(). It should still return false for other cases of blocks, such as // when the package is a system app, or when it is an active device admin. final int userId = UserHandle.getCallingUserId(); synchronized (this) { if (who != null) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } long id = mInjector.binderClearCallingIdentity(); try { return mIPackageManager.getBlockUninstallForUser(packageName, userId); } catch (RemoteException re) { // Shouldn't happen. Slog.e(LOG_TAG, "Failed to getBlockUninstallForUser", re); } finally { mInjector.binderRestoreCallingIdentity(id); } } return false; } @Override public void setCrossProfileCallerIdDisabled(ComponentName who, boolean disabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (admin.disableCallerId != disabled) { admin.disableCallerId = disabled; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public boolean getCrossProfileCallerIdDisabled(ComponentName who) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.disableCallerId; } } @Override public boolean getCrossProfileCallerIdDisabledForUser(int userId) { enforceCrossUsersPermission(userId); synchronized (this) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableCallerId : false; } } @Override public void setCrossProfileContactsSearchDisabled(ComponentName who, boolean disabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (admin.disableContactsSearch != disabled) { admin.disableContactsSearch = disabled; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @Override public boolean getCrossProfileContactsSearchDisabled(ComponentName who) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.disableContactsSearch; } } @Override public boolean getCrossProfileContactsSearchDisabledForUser(int userId) { enforceCrossUsersPermission(userId); synchronized (this) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableContactsSearch : false; } } @Override public void startManagedQuickContact(String actualLookupKey, long actualContactId, boolean isContactIdIgnored, long actualDirectoryId, Intent originalIntent) { final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(actualLookupKey, actualContactId, isContactIdIgnored, actualDirectoryId, originalIntent); final int callingUserId = UserHandle.getCallingUserId(); final long ident = mInjector.binderClearCallingIdentity(); try { synchronized (this) { final int managedUserId = getManagedUserId(callingUserId); if (managedUserId < 0) { return; } if (isCrossProfileQuickContactDisabled(managedUserId)) { if (VERBOSE_LOG) { Log.v(LOG_TAG, "Cross-profile contacts access disabled for user " + managedUserId); } return; } ContactsInternal.startQuickContactWithErrorToastForUser( mContext, intent, new UserHandle(managedUserId)); } } finally { mInjector.binderRestoreCallingIdentity(ident); } } /** * @return true if cross-profile QuickContact is disabled */ private boolean isCrossProfileQuickContactDisabled(int userId) { return getCrossProfileCallerIdDisabledForUser(userId) && getCrossProfileContactsSearchDisabledForUser(userId); } /** * @return the user ID of the managed user that is linked to the current user, if any. * Otherwise -1. */ public int getManagedUserId(int callingUserId) { if (VERBOSE_LOG) { Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId); } for (UserInfo ui : mUserManager.getProfiles(callingUserId)) { if (ui.id == callingUserId || !ui.isManagedProfile()) { continue; // Caller user self, or not a managed profile. Skip. } if (VERBOSE_LOG) { Log.v(LOG_TAG, "Managed user=" + ui.id); } return ui.id; } if (VERBOSE_LOG) { Log.v(LOG_TAG, "Managed user not found."); } return -1; } @Override public void setBluetoothContactSharingDisabled(ComponentName who, boolean disabled) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (admin.disableBluetoothContactSharing != disabled) { admin.disableBluetoothContactSharing = disabled; saveSettingsLocked(UserHandle.getCallingUserId()); } } } @Override public boolean getBluetoothContactSharingDisabled(ComponentName who) { if (!mHasFeature) { return false; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.disableBluetoothContactSharing; } } @Override public boolean getBluetoothContactSharingDisabledForUser(int userId) { // TODO: Should there be a check to make sure this relationship is // within a profile group? // enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system"); synchronized (this) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableBluetoothContactSharing : false; } } /** * Sets which packages may enter lock task mode. * *

This function can only be called by the device owner or alternatively by the profile owner * in case the user is affiliated. * * @param packages The list of packages allowed to enter lock task mode. */ @Override public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin deviceOwner = getActiveAdminWithPolicyForUidLocked( who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, mInjector.binderGetCallingUid()); ActiveAdmin profileOwner = getActiveAdminWithPolicyForUidLocked( who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid()); if (deviceOwner != null || (profileOwner != null && isAffiliatedUser())) { int userHandle = mInjector.userHandleGetCallingUserId(); setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages))); } else { throw new SecurityException("Admin " + who + " is neither the device owner or affiliated user's profile owner."); } } } private void setLockTaskPackagesLocked(int userHandle, List packages) { DevicePolicyData policy = getUserData(userHandle); policy.mLockTaskPackages = packages; // Store the settings persistently. saveSettingsLocked(userHandle); updateLockTaskPackagesLocked(packages, userHandle); } /** * This function returns the list of components allowed to start the task lock mode. */ @Override public String[] getLockTaskPackages(ComponentName who) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier(); final List packages = getLockTaskPackagesLocked(userHandle); return packages.toArray(new String[packages.size()]); } } private List getLockTaskPackagesLocked(int userHandle) { final DevicePolicyData policy = getUserData(userHandle); return policy.mLockTaskPackages; } /** * This function lets the caller know whether the given package is allowed to start the * lock task mode. * @param pkg The package to check */ @Override public boolean isLockTaskPermitted(String pkg) { // Get current user's devicepolicy int uid = mInjector.binderGetCallingUid(); int userHandle = UserHandle.getUserId(uid); DevicePolicyData policy = getUserData(userHandle); synchronized (this) { for (int i = 0; i < policy.mLockTaskPackages.size(); i++) { String lockTaskPackage = policy.mLockTaskPackages.get(i); // If the given package equals one of the packages stored our list, // we allow this package to start lock task mode. if (lockTaskPackage.equals(pkg)) { return true; } } } return false; } @Override public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) { if (!isCallerWithSystemUid()) { throw new SecurityException("notifyLockTaskModeChanged can only be called by system"); } synchronized (this) { final DevicePolicyData policy = getUserData(userHandle); Bundle adminExtras = new Bundle(); adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg); for (ActiveAdmin admin : policy.mAdminList) { final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userHandle); final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userHandle); if (ownsDevice || ownsProfile) { if (isEnabled) { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_ENTERING, adminExtras, null); } else { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_EXITING); } } } } } @Override public void setGlobalSetting(ComponentName who, String setting, String value) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); // Some settings are no supported any more. However we do not want to throw a // SecurityException to avoid breaking apps. if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) { Log.i(LOG_TAG, "Global setting no longer supported: " + setting); return; } if (!GLOBAL_SETTINGS_WHITELIST.contains(setting)) { throw new SecurityException(String.format( "Permission denial: device owners cannot update %1$s", setting)); } if (Settings.Global.STAY_ON_WHILE_PLUGGED_IN.equals(setting)) { // ignore if it contradicts an existing policy long timeMs = getMaximumTimeToLock( who, mInjector.userHandleGetCallingUserId(), /* parent */ false); if (timeMs > 0 && timeMs < Integer.MAX_VALUE) { return; } } long id = mInjector.binderClearCallingIdentity(); try { mInjector.settingsGlobalPutString(setting, value); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public void setSecureSetting(ComponentName who, String setting, String value) { Preconditions.checkNotNull(who, "ComponentName is null"); int callingUserId = mInjector.userHandleGetCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (isDeviceOwner(who, callingUserId)) { if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)) { throw new SecurityException(String.format( "Permission denial: Device owners cannot update %1$s", setting)); } } else if (!SECURE_SETTINGS_WHITELIST.contains(setting)) { throw new SecurityException(String.format( "Permission denial: Profile owners cannot update %1$s", setting)); } long id = mInjector.binderClearCallingIdentity(); try { mInjector.settingsSecurePutStringForUser(setting, value, callingUserId); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public void setMasterVolumeMuted(ComponentName who, boolean on) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); setUserRestriction(who, UserManager.DISALLLOW_UNMUTE_DEVICE, on); } } @Override public boolean isMasterVolumeMuted(ComponentName who) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); return audioManager.isMasterMute(); } } @Override public void setUserIcon(ComponentName who, Bitmap icon) { synchronized (this) { Preconditions.checkNotNull(who, "ComponentName is null"); getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); int userId = UserHandle.getCallingUserId(); long id = mInjector.binderClearCallingIdentity(); try { mUserManagerInternal.setUserIcon(userId, icon); } finally { mInjector.binderRestoreCallingIdentity(id); } } } @Override public boolean setKeyguardDisabled(ComponentName who, boolean disabled) { Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); } final int userId = UserHandle.getCallingUserId(); long ident = mInjector.binderClearCallingIdentity(); try { // disallow disabling the keyguard if a password is currently set if (disabled && mLockPatternUtils.isSecure(userId)) { return false; } mLockPatternUtils.setLockScreenDisabled(disabled, userId); } finally { mInjector.binderRestoreCallingIdentity(ident); } return true; } @Override public boolean setStatusBarDisabled(ComponentName who, boolean disabled) { int userId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); DevicePolicyData policy = getUserData(userId); if (policy.mStatusBarDisabled != disabled) { if (!setStatusBarDisabledInternal(disabled, userId)) { return false; } policy.mStatusBarDisabled = disabled; saveSettingsLocked(userId); } } return true; } private boolean setStatusBarDisabledInternal(boolean disabled, int userId) { long ident = mInjector.binderClearCallingIdentity(); try { IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); if (statusBarService != null) { int flags1 = disabled ? STATUS_BAR_DISABLE_MASK : StatusBarManager.DISABLE_NONE; int flags2 = disabled ? STATUS_BAR_DISABLE2_MASK : StatusBarManager.DISABLE2_NONE; statusBarService.disableForUser(flags1, mToken, mContext.getPackageName(), userId); statusBarService.disable2ForUser(flags2, mToken, mContext.getPackageName(), userId); return true; } } catch (RemoteException e) { Slog.e(LOG_TAG, "Failed to disable the status bar", e); } finally { mInjector.binderRestoreCallingIdentity(ident); } return false; } /** * 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. *

* Unfortunately, we don't know which user's setup state was changed, so we write all of * them. */ void updateUserSetupComplete() { List users = mUserManager.getUsers(true); final int N = users.size(); for (int i = 0; i < N; i++) { int userHandle = users.get(i).id; if (mInjector.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, userHandle) != 0) { DevicePolicyData policy = getUserData(userHandle); if (!policy.mUserSetupComplete) { policy.mUserSetupComplete = true; synchronized (this) { saveSettingsLocked(userHandle); } } } } } private class SetupContentObserver extends ContentObserver { private final Uri mUserSetupComplete = Settings.Secure.getUriFor( Settings.Secure.USER_SETUP_COMPLETE); private final Uri mDeviceProvisioned = Settings.Global.getUriFor( Settings.Global.DEVICE_PROVISIONED); public SetupContentObserver(Handler handler) { super(handler); } void register() { mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL); mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL); } @Override public void onChange(boolean selfChange, Uri uri) { if (mUserSetupComplete.equals(uri)) { updateUserSetupComplete(); } else if (mDeviceProvisioned.equals(uri)) { synchronized (DevicePolicyManagerService.this) { // Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property // is delayed until device is marked as provisioned. setDeviceOwnerSystemPropertyLocked(); } } } } @VisibleForTesting final class LocalService extends DevicePolicyManagerInternal { private List mWidgetProviderListeners; @Override public List getCrossProfileWidgetProviders(int profileId) { synchronized (DevicePolicyManagerService.this) { if (mOwners == null) { return Collections.emptyList(); } ComponentName ownerComponent = mOwners.getProfileOwnerComponent(profileId); if (ownerComponent == null) { return Collections.emptyList(); } DevicePolicyData policy = getUserDataUnchecked(profileId); ActiveAdmin admin = policy.mAdminMap.get(ownerComponent); if (admin == null || admin.crossProfileWidgetProviders == null || admin.crossProfileWidgetProviders.isEmpty()) { return Collections.emptyList(); } return admin.crossProfileWidgetProviders; } } @Override public void addOnCrossProfileWidgetProvidersChangeListener( OnCrossProfileWidgetProvidersChangeListener listener) { synchronized (DevicePolicyManagerService.this) { if (mWidgetProviderListeners == null) { mWidgetProviderListeners = new ArrayList<>(); } if (!mWidgetProviderListeners.contains(listener)) { mWidgetProviderListeners.add(listener); } } } @Override public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) { synchronized(DevicePolicyManagerService.this) { return getActiveAdminWithPolicyForUidLocked(null, reqPolicy, uid) != null; } } private void notifyCrossProfileProvidersChanged(int userId, List packages) { final List listeners; synchronized (DevicePolicyManagerService.this) { listeners = new ArrayList<>(mWidgetProviderListeners); } final int listenerCount = listeners.size(); for (int i = 0; i < listenerCount; i++) { OnCrossProfileWidgetProvidersChangeListener listener = listeners.get(i); listener.onCrossProfileWidgetProvidersChanged(userId, packages); } } @Override public Intent createPackageSuspendedDialogIntent(String packageName, int userId) { Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); intent.putExtra(Intent.EXTRA_USER_ID, userId); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // This method is called from AM with its lock held, so don't take the DPMS lock. // b/29242568 ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId); if (profileOwner != null) { intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, profileOwner); return intent; } final Pair deviceOwner = mOwners.getDeviceOwnerUserIdAndComponent(); if (deviceOwner != null && deviceOwner.first == userId) { intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceOwner.second); return intent; } // We're not specifying the device admin because there isn't one. return intent; } } /** * Returns true if specified admin is allowed to limit passwords and has a * {@code passwordQuality} of at least {@code minPasswordQuality} */ private static boolean isLimitPasswordAllowed(ActiveAdmin admin, int minPasswordQuality) { if (admin.passwordQuality < minPasswordQuality) { return false; } return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); } @Override public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { if (policy != null && !policy.isValid()) { throw new IllegalArgumentException("Invalid system update policy."); } synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); if (policy == null) { mOwners.clearSystemUpdatePolicy(); } else { mOwners.setSystemUpdatePolicy(policy); } mOwners.writeDeviceOwner(); } mContext.sendBroadcastAsUser( new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED), UserHandle.SYSTEM); } @Override public SystemUpdatePolicy getSystemUpdatePolicy() { if (UserManager.isDeviceInDemoMode(mContext)) { // Pretending to have an automatic update policy when the device is in retail demo // mode. This will allow the device to download and install an ota without // any user interaction. return SystemUpdatePolicy.createAutomaticInstallPolicy(); } synchronized (this) { SystemUpdatePolicy policy = mOwners.getSystemUpdatePolicy(); if (policy != null && !policy.isValid()) { Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead."); return null; } return policy; } } /** * Checks if the caller of the method is the device owner app. * * @param callerUid UID of the caller. * @return true if the caller is the device owner app */ @VisibleForTesting boolean isCallerDeviceOwner(int callerUid) { synchronized (this) { if (!mOwners.hasDeviceOwner()) { return false; } if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) { return false; } final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent() .getPackageName(); final String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid); for (String pkg : pkgs) { if (deviceOwnerPackageName.equals(pkg)) { return true; } } } return false; } @Override public void notifyPendingSystemUpdate(long updateReceivedTime) { mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE, "Only the system update service can broadcast update information"); if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) { Slog.w(LOG_TAG, "Only the system update service in the system user " + "can broadcast update information."); return; } Intent intent = new Intent(DeviceAdminReceiver.ACTION_NOTIFY_PENDING_SYSTEM_UPDATE); intent.putExtra(DeviceAdminReceiver.EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, updateReceivedTime); synchronized (this) { final String deviceOwnerPackage = mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerComponent().getPackageName() : null; if (deviceOwnerPackage == null) { return; } final UserHandle deviceOwnerUser = new UserHandle(mOwners.getDeviceOwnerUserId()); ActivityInfo[] receivers = null; try { receivers = mContext.getPackageManager().getPackageInfo( deviceOwnerPackage, PackageManager.GET_RECEIVERS).receivers; } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Cannot find device owner package", e); } if (receivers != null) { long ident = mInjector.binderClearCallingIdentity(); try { for (int i = 0; i < receivers.length; i++) { if (permission.BIND_DEVICE_ADMIN.equals(receivers[i].permission)) { intent.setComponent(new ComponentName(deviceOwnerPackage, receivers[i].name)); mContext.sendBroadcastAsUser(intent, deviceOwnerUser); } } } finally { mInjector.binderRestoreCallingIdentity(ident); } } } } @Override public void setPermissionPolicy(ComponentName admin, int policy) throws RemoteException { int userId = UserHandle.getCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); DevicePolicyData userPolicy = getUserData(userId); if (userPolicy.mPermissionPolicy != policy) { userPolicy.mPermissionPolicy = policy; saveSettingsLocked(userId); } } } @Override public int getPermissionPolicy(ComponentName admin) throws RemoteException { int userId = UserHandle.getCallingUserId(); synchronized (this) { DevicePolicyData userPolicy = getUserData(userId); return userPolicy.mPermissionPolicy; } } @Override public boolean setPermissionGrantState(ComponentName admin, String packageName, String permission, int grantState) throws RemoteException { UserHandle user = mInjector.binderGetCallingUserHandle(); synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long ident = mInjector.binderClearCallingIdentity(); try { if (getTargetSdk(packageName, user.getIdentifier()) < android.os.Build.VERSION_CODES.M) { return false; } final PackageManager packageManager = mContext.getPackageManager(); switch (grantState) { case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: { packageManager.grantRuntimePermission(packageName, permission, user); packageManager.updatePermissionFlags(permission, packageName, PackageManager.FLAG_PERMISSION_POLICY_FIXED, PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); } break; case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: { packageManager.revokeRuntimePermission(packageName, permission, user); packageManager.updatePermissionFlags(permission, packageName, PackageManager.FLAG_PERMISSION_POLICY_FIXED, PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); } break; case DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT: { packageManager.updatePermissionFlags(permission, packageName, PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0, user); } break; } return true; } catch (SecurityException se) { return false; } finally { mInjector.binderRestoreCallingIdentity(ident); } } } @Override public int getPermissionGrantState(ComponentName admin, String packageName, String permission) throws RemoteException { PackageManager packageManager = mContext.getPackageManager(); UserHandle user = mInjector.binderGetCallingUserHandle(); synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long ident = mInjector.binderClearCallingIdentity(); try { int granted = mIPackageManager.checkPermission(permission, packageName, user.getIdentifier()); int permFlags = packageManager.getPermissionFlags(permission, packageName, user); if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != PackageManager.FLAG_PERMISSION_POLICY_FIXED) { // Not controlled by policy return DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT; } else { // Policy controlled so return result based on permission grant state return granted == PackageManager.PERMISSION_GRANTED ? DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED : DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED; } } finally { mInjector.binderRestoreCallingIdentity(ident); } } } boolean isPackageInstalledForUser(String packageName, int userHandle) { try { PackageInfo pi = mInjector.getIPackageManager().getPackageInfo(packageName, 0, userHandle); return (pi != null) && (pi.applicationInfo.flags != 0); } catch (RemoteException re) { throw new RuntimeException("Package manager has died", re); } } @Override public boolean isProvisioningAllowed(String action) { if (!mHasFeature) { return false; } final int callingUserId = mInjector.userHandleGetCallingUserId(); if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) { if (!hasFeatureManagedUsers()) { return false; } synchronized (this) { if (mOwners.hasDeviceOwner()) { if (!mInjector.userManagerIsSplitSystemUser()) { // Only split-system-user systems support managed-profiles in combination with // device-owner. return false; } if (mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM) { // Only system device-owner supports managed-profiles. Non-system device-owner // doesn't. return false; } if (callingUserId == UserHandle.USER_SYSTEM) { // Managed-profiles cannot be setup on the system user, only regular users. return false; } } } if (getProfileOwner(callingUserId) != null) { // Managed user cannot have a managed profile. return false; } final long ident = mInjector.binderClearCallingIdentity(); try { if (!mUserManager.canAddMoreManagedProfiles(callingUserId, true)) { return false; } } finally { mInjector.binderRestoreCallingIdentity(ident); } return true; } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE.equals(action)) { return isDeviceOwnerProvisioningAllowed(callingUserId); } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_USER.equals(action)) { if (!hasFeatureManagedUsers()) { return false; } if (!mInjector.userManagerIsSplitSystemUser()) { // ACTION_PROVISION_MANAGED_USER only supported on split-user systems. return false; } if (callingUserId == UserHandle.USER_SYSTEM) { // System user cannot be a managed user. return false; } if (hasUserSetupCompleted(callingUserId)) { return false; } return true; } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE.equals(action)) { if (!mInjector.userManagerIsSplitSystemUser()) { // ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE only supported on split-user systems. return false; } return isDeviceOwnerProvisioningAllowed(callingUserId); } throw new IllegalArgumentException("Unknown provisioning action " + action); } /* * 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 checkSetDeviceOwnerPreConditionLocked( @Nullable ComponentName owner, int deviceOwnerUserId, boolean isAdb) { if (mOwners.hasDeviceOwner()) { return CODE_HAS_DEVICE_OWNER; } if (mOwners.hasProfileOwner(deviceOwnerUserId)) { return CODE_USER_HAS_PROFILE_OWNER; } if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) { return CODE_USER_NOT_RUNNING; } if (isAdb) { // if shell command runs after user setup completed check device status. Otherwise, OK. if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { if (!mInjector.userManagerIsSplitSystemUser()) { if (mUserManager.getUserCount() > 1) { return CODE_NONSYSTEM_USER_EXISTS; } if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) { return CODE_ACCOUNTS_NOT_EMPTY; } } else { // STOPSHIP Do proper check in split user mode } } return CODE_OK; } else { if (!mInjector.userManagerIsSplitSystemUser()) { // In non-split user mode, DO has to be user 0 if (deviceOwnerUserId != UserHandle.USER_SYSTEM) { return CODE_NOT_SYSTEM_USER; } // In non-split user mode, only provision DO before setup wizard completes if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { return CODE_USER_SETUP_COMPLETED; } } else { // STOPSHIP Do proper check in split user mode } return CODE_OK; } } private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) { synchronized (this) { return CODE_OK == checkSetDeviceOwnerPreConditionLocked( /* owner unknown */ null, deviceOwnerUserId, /* isAdb */ false); } } private boolean hasFeatureManagedUsers() { try { return mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0); } catch (RemoteException e) { return false; } } @Override public String getWifiMacAddress(ComponentName admin) { // Make sure caller has DO. synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); } final long ident = mInjector.binderClearCallingIdentity(); try { final WifiInfo wifiInfo = mInjector.getWifiManager().getConnectionInfo(); if (wifiInfo == null) { return null; } return wifiInfo.hasRealMacAddress() ? wifiInfo.getMacAddress() : null; } finally { mInjector.binderRestoreCallingIdentity(ident); } } /** * Returns the target sdk version number that the given packageName was built for * in the given user. */ private int getTargetSdk(String packageName, int userId) { final ApplicationInfo ai; try { ai = mIPackageManager.getApplicationInfo(packageName, 0, userId); final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion; return targetSdkVersion; } catch (RemoteException e) { // Shouldn't happen return 0; } } @Override public boolean isManagedProfile(ComponentName admin) { synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); } final int callingUserId = mInjector.userHandleGetCallingUserId(); final UserInfo user = getUserInfo(callingUserId); return user != null && user.isManagedProfile(); } @Override public boolean isSystemOnlyUser(ComponentName admin) { synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); } final int callingUserId = mInjector.userHandleGetCallingUserId(); return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM; } @Override public void reboot(ComponentName admin) { Preconditions.checkNotNull(admin); // Make sure caller has DO. synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); } long ident = mInjector.binderClearCallingIdentity(); try { // Make sure there are no ongoing calls on the device. if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { throw new IllegalStateException("Cannot be called with ongoing call on the device"); } mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER); } finally { mInjector.binderRestoreCallingIdentity(ident); } } @Override public void setShortSupportMessage(@NonNull ComponentName who, CharSequence message) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); if (!TextUtils.equals(admin.shortSupportMessage, message)) { admin.shortSupportMessage = message; saveSettingsLocked(userHandle); } } } @Override public CharSequence getShortSupportMessage(@NonNull ComponentName who) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); return admin.shortSupportMessage; } } @Override public void setLongSupportMessage(@NonNull ComponentName who, CharSequence message) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (this) { ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); if (!TextUtils.equals(admin.longSupportMessage, message)) { admin.longSupportMessage = message; saveSettingsLocked(userHandle); } } } @Override public CharSequence getLongSupportMessage(@NonNull ComponentName who) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); synchronized (this) { ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); return admin.longSupportMessage; } } @Override public CharSequence getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); if (!isCallerWithSystemUid()) { throw new SecurityException("Only the system can query support message for user"); } synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin != null) { return admin.shortSupportMessage; } } return null; } @Override public CharSequence getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); if (!isCallerWithSystemUid()) { throw new SecurityException("Only the system can query support message for user"); } synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin != null) { return admin.longSupportMessage; } } return null; } @Override public void setOrganizationColor(@NonNull ComponentName who, int color) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); enforceManagedProfile(userHandle, "set organization color"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); admin.organizationColor = color; saveSettingsLocked(userHandle); } } @Override public void setOrganizationColorForUser(int color, int userId) { if (!mHasFeature) { return; } enforceFullCrossUsersPermission(userId); enforceManageUsers(); enforceManagedProfile(userId, "set organization color"); synchronized (this) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); admin.organizationColor = color; saveSettingsLocked(userId); } } @Override public int getOrganizationColor(@NonNull ComponentName who) { if (!mHasFeature) { return ActiveAdmin.DEF_ORGANIZATION_COLOR; } Preconditions.checkNotNull(who, "ComponentName is null"); enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization color"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.organizationColor; } } @Override public int getOrganizationColorForUser(int userHandle) { if (!mHasFeature) { return ActiveAdmin.DEF_ORGANIZATION_COLOR; } enforceFullCrossUsersPermission(userHandle); enforceManagedProfile(userHandle, "get organization color"); synchronized (this) { ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle); return (profileOwner != null) ? profileOwner.organizationColor : ActiveAdmin.DEF_ORGANIZATION_COLOR; } } @Override public void setOrganizationName(@NonNull ComponentName who, CharSequence text) { if (!mHasFeature) { return; } Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); enforceManagedProfile(userHandle, "set organization name"); synchronized (this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (!TextUtils.equals(admin.organizationName, text)) { admin.organizationName = (text == null || text.length() == 0) ? null : text.toString(); saveSettingsLocked(userHandle); } } } @Override public CharSequence getOrganizationName(@NonNull ComponentName who) { if (!mHasFeature) { return null; } Preconditions.checkNotNull(who, "ComponentName is null"); enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization name"); synchronized(this) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); return admin.organizationName; } } @Override public CharSequence getOrganizationNameForUser(int userHandle) { if (!mHasFeature) { return null; } enforceFullCrossUsersPermission(userHandle); enforceManagedProfile(userHandle, "get organization name"); synchronized (this) { ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle); return (profileOwner != null) ? profileOwner.organizationName : null; } } @Override public void setAffiliationIds(ComponentName admin, List ids) { final Set affiliationIds = new ArraySet(ids); final int callingUserId = mInjector.userHandleGetCallingUserId(); synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); getUserData(callingUserId).mAffiliationIds = affiliationIds; saveSettingsLocked(callingUserId); if (callingUserId != UserHandle.USER_SYSTEM && isDeviceOwner(admin, callingUserId)) { // Affiliation ids specified by the device owner are additionally stored in // UserHandle.USER_SYSTEM's DevicePolicyData. getUserData(UserHandle.USER_SYSTEM).mAffiliationIds = affiliationIds; saveSettingsLocked(UserHandle.USER_SYSTEM); } } } @Override public boolean isAffiliatedUser() { final int callingUserId = mInjector.userHandleGetCallingUserId(); synchronized (this) { if (mOwners.getDeviceOwnerUserId() == callingUserId) { // The user that the DO is installed on is always affiliated. return true; } final ComponentName profileOwner = getProfileOwner(callingUserId); if (profileOwner == null || !profileOwner.getPackageName().equals(mOwners.getDeviceOwnerPackageName())) { return false; } final Set userAffiliationIds = getUserData(callingUserId).mAffiliationIds; final Set deviceAffiliationIds = getUserData(UserHandle.USER_SYSTEM).mAffiliationIds; for (String id : userAffiliationIds) { if (deviceAffiliationIds.contains(id)) { return true; } } } return false; } private synchronized void disableDeviceOwnerManagedSingleUserFeaturesIfNeeded() { if (!isDeviceOwnerManagedSingleUserDevice()) { mInjector.securityLogSetLoggingEnabledProperty(false); Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user device."); if (mOwners.hasDeviceOwner()) { setBackupServiceEnabledInternal(false); Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user."); } } } @Override public void setSecurityLoggingEnabled(ComponentName admin, boolean enabled) { Preconditions.checkNotNull(admin); ensureDeviceOwnerManagingSingleUser(admin); synchronized (this) { if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) { return; } mInjector.securityLogSetLoggingEnabledProperty(enabled); if (enabled) { mSecurityLogMonitor.start(); } else { mSecurityLogMonitor.stop(); } } } @Override public boolean isSecurityLoggingEnabled(ComponentName admin) { Preconditions.checkNotNull(admin); synchronized (this) { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); return mInjector.securityLogGetLoggingEnabledProperty(); } } @Override public ParceledListSlice retrievePreRebootSecurityLogs(ComponentName admin) { Preconditions.checkNotNull(admin); ensureDeviceOwnerManagingSingleUser(admin); if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)) { return null; } ArrayList output = new ArrayList(); try { SecurityLog.readPreviousEvents(output); return new ParceledListSlice(output); } catch (IOException e) { Slog.w(LOG_TAG, "Fail to read previous events" , e); return new ParceledListSlice(Collections.emptyList()); } } @Override public ParceledListSlice retrieveSecurityLogs(ComponentName admin) { Preconditions.checkNotNull(admin); ensureDeviceOwnerManagingSingleUser(admin); List logs = mSecurityLogMonitor.retrieveLogs(); return logs != null ? new ParceledListSlice(logs) : null; } private void enforceCanManageDeviceAdmin() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); } private void enforceCanManageProfileAndDeviceOwners() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null); } @Override public boolean isUninstallInQueue(final String packageName) { enforceCanManageDeviceAdmin(); final int userId = mInjector.userHandleGetCallingUserId(); Pair packageUserPair = new Pair<>(packageName, userId); synchronized (this) { return mPackagesToRemove.contains(packageUserPair); } } @Override public void uninstallPackageWithActiveAdmins(final String packageName) { enforceCanManageDeviceAdmin(); Preconditions.checkArgument(!TextUtils.isEmpty(packageName)); final int userId = mInjector.userHandleGetCallingUserId(); enforceUserUnlocked(userId); final ComponentName profileOwner = getProfileOwner(userId); if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) { throw new IllegalArgumentException("Cannot uninstall a package with a profile owner"); } final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false); if (getDeviceOwnerUserId() == userId && deviceOwner != null && packageName.equals(deviceOwner.getPackageName())) { throw new IllegalArgumentException("Cannot uninstall a package with a device owner"); } final Pair packageUserPair = new Pair<>(packageName, userId); synchronized (this) { mPackagesToRemove.add(packageUserPair); } // All active admins on the user. final List allActiveAdmins = getActiveAdmins(userId); // Active admins in the target package. final List packageActiveAdmins = new ArrayList<>(); if (allActiveAdmins != null) { for (ComponentName activeAdmin : allActiveAdmins) { if (packageName.equals(activeAdmin.getPackageName())) { packageActiveAdmins.add(activeAdmin); removeActiveAdmin(activeAdmin, userId); } } } if (packageActiveAdmins.size() == 0) { startUninstallIntent(packageName, userId); } else { mHandler.postDelayed(new Runnable() { @Override public void run() { for (ComponentName activeAdmin : packageActiveAdmins) { removeAdminArtifacts(activeAdmin, userId); } startUninstallIntent(packageName, userId); } }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway. } } @Override public boolean isDeviceProvisioned() { return !TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT)); } private void removePackageIfRequired(final String packageName, final int userId) { if (!packageHasActiveAdmins(packageName, userId)) { // Will not do anything if uninstall was not requested or was already started. startUninstallIntent(packageName, userId); } } private void startUninstallIntent(final String packageName, final int userId) { final Pair packageUserPair = new Pair<>(packageName, userId); synchronized (this) { if (!mPackagesToRemove.contains(packageUserPair)) { // Do nothing if uninstall was not requested or was already started. return; } mPackagesToRemove.remove(packageUserPair); } try { if (mInjector.getIPackageManager().getPackageInfo(packageName, 0, userId) == null) { // Package does not exist. Nothing to do. return; } } catch (RemoteException re) { Log.e(LOG_TAG, "Failure talking to PackageManager while getting package info"); } try { // force stop the package before uninstalling mInjector.getIActivityManager().forceStopPackage(packageName, userId); } catch (RemoteException re) { Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package"); } final Uri packageURI = Uri.parse("package:" + packageName); final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(uninstallIntent, UserHandle.of(userId)); } /** * Removes the admin from the policy. Ideally called after the admin's * {@link DeviceAdminReceiver#onDisabled(Context, Intent)} has been successfully completed. * * @param adminReceiver The admin to remove * @param userHandle The user for which this admin has to be removed. */ private void removeAdminArtifacts(final ComponentName adminReceiver, final int userHandle) { synchronized (this) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin == null) { return; } final DevicePolicyData policy = getUserData(userHandle); final boolean doProxyCleanup = admin.info.usesPolicy( DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); policy.mAdminList.remove(admin); policy.mAdminMap.remove(adminReceiver); validatePasswordOwnerLocked(policy); if (doProxyCleanup) { resetGlobalProxyLocked(policy); } 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. pushUserRestrictions(userHandle); } @Override public void setDeviceProvisioningConfigApplied() { enforceManageUsers(); synchronized (this) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); policy.mDeviceProvisioningConfigApplied = true; saveSettingsLocked(UserHandle.USER_SYSTEM); } } @Override public boolean isDeviceProvisioningConfigApplied() { enforceManageUsers(); synchronized (this) { final DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); 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. */ private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) { final long token = mInjector.binderClearCallingIdentity(); try { final AccountManager am = AccountManager.get(mContext); final Account accounts[] = am.getAccountsAsUser(userId); if (accounts.length == 0) { return false; } final String[] feature_allow = { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED }; final String[] feature_disallow = { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED }; // Even if we find incompatible accounts along the way, we still check all accounts // for logging. boolean compatible = true; for (Account account : accounts) { if (hasAccountFeatures(am, account, feature_disallow)) { Log.e(LOG_TAG, account + " has " + feature_disallow[0]); compatible = false; } if (!hasAccountFeatures(am, account, feature_allow)) { Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]); compatible = false; } } if (compatible) { Log.w(LOG_TAG, "All accounts are compatible"); } else { Log.e(LOG_TAG, "Found incompatible accounts"); } // Then check if the owner is test-only. String log; if (owner == null) { // Owner is unknown. Suppose it's not test-only compatible = false; log = "Only test-only device/profile owner can be installed with accounts"; } else if (isAdminTestOnlyLocked(owner, userId)) { if (compatible) { log = "Installing test-only owner " + owner; } else { log = "Can't install test-only owner " + owner + " with incompatible accounts"; } } else { compatible = false; log = "Can't install non test-only owner " + owner + " with accounts"; } if (compatible) { Log.w(LOG_TAG, log); } else { Log.e(LOG_TAG, log); } 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); } } } }