summaryrefslogtreecommitdiffstats
path: root/services/devicepolicy
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2013-12-19 23:30:35 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2013-12-19 23:30:35 +0000
commit49782e46c0eb85a25ae2abcf80880c48dbab5aea (patch)
tree9fab2a40c41004d78b7001dc766d85f61d24f582 /services/devicepolicy
parent4dace6f66d498c2d119adf265776aa83b28452af (diff)
parent9158825f9c41869689d6b1786d7c7aa8bdd524ce (diff)
downloadframeworks_base-49782e46c0eb85a25ae2abcf80880c48dbab5aea.zip
frameworks_base-49782e46c0eb85a25ae2abcf80880c48dbab5aea.tar.gz
frameworks_base-49782e46c0eb85a25ae2abcf80880c48dbab5aea.tar.bz2
am 9158825f: Move some system services to separate directories
* commit '9158825f9c41869689d6b1786d7c7aa8bdd524ce': Move some system services to separate directories
Diffstat (limited to 'services/devicepolicy')
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2960
-rw-r--r--services/devicepolicy/java/service.mk11
2 files changed, 2971 insertions, 0 deletions
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
new file mode 100644
index 0000000..186fbe1
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -0,0 +1,2960 @@
+/*
+ * 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 com.android.internal.R;
+import com.android.internal.os.storage.ExternalStorageFormatter;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.org.conscrypt.TrustedCertificateStore;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.IDevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.net.ProxyProperties;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.PowerManager;
+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.provider.Settings;
+import android.security.Credentials;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.IWindowManager;
+
+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.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Implementation of the device policy APIs.
+ */
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+
+ private static final String LOG_TAG = "DevicePolicyManagerService";
+
+ private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+
+ 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
+
+ protected static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
+ = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
+
+ private static final int MONITORING_CERT_NOTIFICATION_ID = R.string.ssl_ca_cert_warning;
+
+ private static final boolean DBG = false;
+
+ final Context mContext;
+ final PowerManager.WakeLock mWakeLock;
+
+ IPowerManager mIPowerManager;
+ IWindowManager mIWindowManager;
+ NotificationManager mNotificationManager;
+
+ private DeviceOwner mDeviceOwner;
+
+ /**
+ * Whether or not device admin feature is supported. If it isn't return defaults for all
+ * public methods.
+ */
+ private boolean mHasFeature;
+
+ 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;
+
+ final HashMap<ComponentName, ActiveAdmin> mAdminMap
+ = new HashMap<ComponentName, ActiveAdmin>();
+ final ArrayList<ActiveAdmin> mAdminList
+ = new ArrayList<ActiveAdmin>();
+
+ public DevicePolicyData(int userHandle) {
+ mUserHandle = userHandle;
+ }
+ }
+
+ final SparseArray<DevicePolicyData> mUserData = new SparseArray<DevicePolicyData>();
+
+ Handler mHandler = new Handler();
+
+ 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)
+ || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
+ if (DBG) Slog.v(LOG_TAG, "Sending password expiration notifications for action "
+ + action + " for user " + userHandle);
+ mHandler.post(new Runnable() {
+ public void run() {
+ handlePasswordExpirationNotification(getUserData(userHandle));
+ }
+ });
+ }
+ if (Intent.ACTION_BOOT_COMPLETED.equals(action)
+ || KeyChain.ACTION_STORAGE_CHANGED.equals(action)) {
+ manageMonitoringCertificateNotification(intent);
+ }
+ if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUserData(userHandle);
+ } else if (Intent.ACTION_USER_STARTED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+
+ if (Intent.ACTION_USER_STARTED.equals(action)) {
+ // Reset the policy data
+ synchronized (DevicePolicyManagerService.this) {
+ mUserData.remove(userHandle);
+ }
+ }
+
+ handlePackagesChanged(userHandle);
+ }
+ }
+ };
+
+ static class ActiveAdmin {
+ private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
+ private static final String TAG_DISABLE_CAMERA = "disable-camera";
+ private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
+ 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_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_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";
+
+ 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;
+
+ 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 disableCamera = false;
+
+ // TODO: review implementation decisions with frameworks team
+ boolean specifiesGlobalProxy = false;
+ String globalProxySpec = null;
+ String globalProxyExclusionList = null;
+
+ ActiveAdmin(DeviceAdminInfo _info) {
+ info = _info;
+ }
+
+ int getUid() { return info.getActivityInfo().applicationInfo.uid; }
+
+ public UserHandle getUserHandle() {
+ return new UserHandle(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 (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 (disableCamera) {
+ out.startTag(null, TAG_DISABLE_CAMERA);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
+ out.endTag(null, TAG_DISABLE_CAMERA);
+ }
+ 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);
+ }
+ }
+
+ void readFromXml(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.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_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_DISABLE_CAMERA.equals(tag)) {
+ disableCamera = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
+ disabledKeyguardFeatures = Integer.parseInt(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ } else {
+ Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("uid="); pw.println(getUid());
+ pw.print(prefix); pw.println("policies:");
+ ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
+ if (pols != null) {
+ for (int i=0; i<pols.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(pols.get(i).tag);
+ }
+ }
+ pw.print(prefix); pw.print("passwordQuality=0x");
+ pw.println(Integer.toHexString(passwordQuality));
+ pw.print(prefix); pw.print("minimumPasswordLength=");
+ pw.println(minimumPasswordLength);
+ pw.print(prefix); pw.print("passwordHistoryLength=");
+ pw.println(passwordHistoryLength);
+ pw.print(prefix); pw.print("minimumPasswordUpperCase=");
+ pw.println(minimumPasswordUpperCase);
+ pw.print(prefix); pw.print("minimumPasswordLowerCase=");
+ pw.println(minimumPasswordLowerCase);
+ pw.print(prefix); pw.print("minimumPasswordLetters=");
+ pw.println(minimumPasswordLetters);
+ pw.print(prefix); pw.print("minimumPasswordNumeric=");
+ pw.println(minimumPasswordNumeric);
+ pw.print(prefix); pw.print("minimumPasswordSymbols=");
+ pw.println(minimumPasswordSymbols);
+ pw.print(prefix); pw.print("minimumPasswordNonLetter=");
+ pw.println(minimumPasswordNonLetter);
+ pw.print(prefix); pw.print("maximumTimeToUnlock=");
+ pw.println(maximumTimeToUnlock);
+ pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
+ pw.println(maximumFailedPasswordsForWipe);
+ pw.print(prefix); pw.print("specifiesGlobalProxy=");
+ pw.println(specifiesGlobalProxy);
+ pw.print(prefix); pw.print("passwordExpirationTimeout=");
+ pw.println(passwordExpirationTimeout);
+ pw.print(prefix); pw.print("passwordExpirationDate=");
+ pw.println(passwordExpirationDate);
+ if (globalProxySpec != null) {
+ pw.print(prefix); pw.print("globalProxySpec=");
+ pw.println(globalProxySpec);
+ }
+ if (globalProxyExclusionList != null) {
+ pw.print(prefix); pw.print("globalProxyEclusionList=");
+ pw.println(globalProxyExclusionList);
+ }
+ pw.print(prefix); pw.print("encryptionRequested=");
+ pw.println(encryptionRequested);
+ pw.print(prefix); pw.print("disableCamera=");
+ pw.println(disableCamera);
+ pw.print(prefix); pw.print("disabledKeyguardFeatures=");
+ pw.println(disabledKeyguardFeatures);
+ }
+ }
+
+ private void handlePackagesChanged(int userHandle) {
+ boolean removed = false;
+ if (DBG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
+ DevicePolicyData policy = getUserData(userHandle);
+ IPackageManager pm = AppGlobals.getPackageManager();
+ for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
+ ActiveAdmin aa = policy.mAdminList.get(i);
+ try {
+ if (pm.getPackageInfo(aa.info.getPackageName(), 0, userHandle) == null
+ || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle) == null) {
+ removed = true;
+ policy.mAdminList.remove(i);
+ policy.mAdminMap.remove(aa.info.getComponent());
+ }
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ }
+ }
+ if (removed) {
+ validatePasswordOwnerLocked(policy);
+ syncDeviceCapabilitiesLocked(policy);
+ saveSettingsLocked(policy.mUserHandle);
+ }
+ }
+
+ /**
+ * Instantiates the service.
+ */
+ public DevicePolicyManagerService(Context context) {
+ mContext = context;
+ mHasFeature = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_DEVICE_ADMIN);
+ mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
+ 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_REMOVED);
+ filter.addAction(Intent.ACTION_USER_STARTED);
+ filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
+ context.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");
+ context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+ }
+
+ /**
+ * Creates and loads the policy data from xml.
+ * @param userHandle the user for whom to load the policy data
+ * @return
+ */
+ 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;
+ }
+ }
+
+ void removeUserData(int userHandle) {
+ synchronized (this) {
+ if (userHandle == UserHandle.USER_OWNER) {
+ Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
+ return;
+ }
+ DevicePolicyData policy = mUserData.get(userHandle);
+ if (policy != null) {
+ mUserData.remove(userHandle);
+ }
+ File policyFile = new File(Environment.getUserSystemDirectory(userHandle),
+ DEVICE_POLICIES_XML);
+ policyFile.delete();
+ Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
+ }
+ }
+
+ void loadDeviceOwner() {
+ synchronized (this) {
+ if (DeviceOwner.isRegistered()) {
+ mDeviceOwner = new DeviceOwner();
+ }
+ }
+ }
+
+ /**
+ * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
+ * reminders. Clears alarm if no expirations are configured.
+ */
+ protected void setExpirationAlarmCheckLocked(Context context, DevicePolicyData policy) {
+ final long expiration = getPasswordExpirationLocked(null, policy.mUserHandle);
+ 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 = Binder.clearCallingIdentity();
+ try {
+ 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,
+ new UserHandle(policy.mUserHandle));
+ am.cancel(pi);
+ if (alarmTime != 0) {
+ am.set(AlarmManager.RTC, alarmTime, pi);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private IPowerManager getIPowerManager() {
+ if (mIPowerManager == null) {
+ IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
+ mIPowerManager = IPowerManager.Stub.asInterface(b);
+ }
+ return mIPowerManager;
+ }
+
+ private IWindowManager getWindowManager() {
+ if (mIWindowManager == null) {
+ IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE);
+ mIWindowManager = IWindowManager.Stub.asInterface(b);
+ }
+ return mIWindowManager;
+ }
+
+ private NotificationManager getNotificationManager() {
+ if (mNotificationManager == null) {
+ mNotificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+ return mNotificationManager;
+ }
+
+ 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 getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
+ throws SecurityException {
+ final int callingUid = Binder.getCallingUid();
+ final int userHandle = UserHandle.getUserId(callingUid);
+ final DevicePolicyData policy = getUserData(userHandle);
+ if (who != null) {
+ ActiveAdmin admin = policy.mAdminMap.get(who);
+ if (admin == null) {
+ throw new SecurityException("No active admin " + who);
+ }
+ if (admin.getUid() != callingUid) {
+ throw new SecurityException("Admin " + who + " is not owned by uid "
+ + Binder.getCallingUid());
+ }
+ if (!admin.info.usesPolicy(reqPolicy)) {
+ throw new SecurityException("Admin " + admin.info.getComponent()
+ + " did not specify uses-policy for: "
+ + admin.info.getTagForPolicy(reqPolicy));
+ }
+ return admin;
+ } else {
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) {
+ return admin;
+ }
+ }
+ throw new SecurityException("No active admin owned by uid "
+ + Binder.getCallingUid() + " for policy #" + reqPolicy);
+ }
+ }
+
+ void sendAdminCommandLocked(ActiveAdmin admin, String action) {
+ sendAdminCommandLocked(admin, action, null);
+ }
+
+ void sendAdminCommandLocked(ActiveAdmin admin, String action, 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 (result != null) {
+ mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(),
+ null, result, mHandler, Activity.RESULT_OK, null, null);
+ } else {
+ mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ }
+ }
+
+ 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++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.info.usesPolicy(reqPolicy)) {
+ sendAdminCommandLocked(admin, action);
+ }
+ }
+ }
+ }
+
+ void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (admin != null) {
+ sendAdminCommandLocked(admin,
+ DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (DevicePolicyManagerService.this) {
+ int userHandle = admin.getUserHandle().getIdentifier();
+ DevicePolicyData policy = getUserData(userHandle);
+ boolean doProxyCleanup = admin.info.usesPolicy(
+ DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+ policy.mAdminList.remove(admin);
+ policy.mAdminMap.remove(adminReceiver);
+ validatePasswordOwnerLocked(policy);
+ syncDeviceCapabilitiesLocked(policy);
+ if (doProxyCleanup) {
+ resetGlobalProxyLocked(getUserData(userHandle));
+ }
+ saveSettingsLocked(userHandle);
+ updateMaximumTimeToLockLocked(policy);
+ }
+ }
+ });
+ }
+ }
+
+ public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle) {
+ if (!mHasFeature) {
+ return null;
+ }
+ enforceCrossUserPermission(userHandle);
+ Intent resolveIntent = new Intent();
+ resolveIntent.setComponent(adminName);
+ List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
+ resolveIntent,
+ PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ userHandle);
+ if (infos == null || infos.size() <= 0) {
+ throw new IllegalArgumentException("Unknown admin: " + adminName);
+ }
+
+ try {
+ return new DeviceAdminInfo(mContext, infos.get(0));
+ } catch (XmlPullParserException e) {
+ Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
+ e);
+ return null;
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
+ e);
+ return null;
+ }
+ }
+
+ private static JournaledFile makeJournaledFile(int userHandle) {
+ final String base = userHandle == 0
+ ? "/data/system/" + DEVICE_POLICIES_XML
+ : new File(Environment.getUserSystemDirectory(userHandle), DEVICE_POLICIES_XML)
+ .getAbsolutePath();
+ 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, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "policies");
+
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin ap = policy.mAdminList.get(i);
+ if (ap != null) {
+ out.startTag(null, "admin");
+ out.attribute(null, "name", ap.info.getComponent().flattenToString());
+ ap.writeToXml(out);
+ out.endTag(null, "admin");
+ }
+ }
+
+ if (policy.mPasswordOwner >= 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");
+ }
+
+ out.endTag(null, "policies");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ sendChangedNotification(userHandle);
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
+ private void sendChangedNotification(int userHandle) {
+ Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void loadSettingsLocked(DevicePolicyData policy, int userHandle) {
+ JournaledFile journal = makeJournaledFile(userHandle);
+ FileInputStream stream = null;
+ File file = journal.chooseForRead();
+ try {
+ stream = new FileInputStream(file);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+ String tag = parser.getName();
+ if (!"policies".equals(tag)) {
+ throw new XmlPullParserException(
+ "Settings do not start with policies tag: found " + tag);
+ }
+ type = parser.next();
+ int outerDepth = parser.getDepth();
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > 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);
+ if (DBG && (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);
+ ap.readFromXml(parser);
+ policy.mAdminMap.put(ap.info.getComponent(), ap);
+ policy.mAdminList.add(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"));
+ XmlUtils.skipCurrentTag(parser);
+ } else if ("password-owner".equals(tag)) {
+ policy.mPasswordOwner = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ XmlUtils.skipCurrentTag(parser);
+ } 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"));
+ XmlUtils.skipCurrentTag(parser);
+ } else {
+ Slog.w(LOG_TAG, "Unknown tag: " + tag);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ } catch (NullPointerException e) {
+ Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
+ } catch (FileNotFoundException e) {
+ // Don't be noisy, this is normal if we haven't defined any policies.
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ // 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.
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ if (utils.getActivePasswordQuality() < policy.mActivePasswordQuality) {
+ Slog.w(LOG_TAG, "Active password quality 0x"
+ + Integer.toHexString(policy.mActivePasswordQuality)
+ + " does not match actual quality 0x"
+ + Integer.toHexString(utils.getActivePasswordQuality()));
+ 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;
+ }
+
+ validatePasswordOwnerLocked(policy);
+ syncDeviceCapabilitiesLocked(policy);
+ updateMaximumTimeToLockLocked(policy);
+ }
+
+ 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_ALPHABETIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+ 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;
+ }
+ }
+ }
+
+ /**
+ * Pushes down policy information to the system for any policies related to general device
+ * capabilities that need to be enforced by lower level services (e.g. Camera services).
+ */
+ void syncDeviceCapabilitiesLocked(DevicePolicyData policy) {
+ // Ensure the status of the camera is synced down to the system. Interested native services
+ // should monitor this value and act accordingly.
+ boolean systemState = SystemProperties.getBoolean(SYSTEM_PROP_DISABLE_CAMERA, false);
+ boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle);
+ if (cameraDisabled != systemState) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ String value = cameraDisabled ? "1" : "0";
+ if (DBG) Slog.v(LOG_TAG, "Change in camera state ["
+ + SYSTEM_PROP_DISABLE_CAMERA + "] = " + value);
+ SystemProperties.set(SYSTEM_PROP_DISABLE_CAMERA, value);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ public void systemReady() {
+ if (!mHasFeature) {
+ return;
+ }
+ synchronized (this) {
+ loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER);
+ loadDeviceOwner();
+ }
+ }
+
+ private void handlePasswordExpirationNotification(DevicePolicyData policy) {
+ synchronized (this) {
+ final long now = System.currentTimeMillis();
+ final int N = policy.mAdminList.size();
+ if (N <= 0) {
+ return;
+ }
+ for (int i=0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+ && admin.passwordExpirationTimeout > 0L
+ && admin.passwordExpirationDate > 0L
+ && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
+ sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+ }
+ }
+ setExpirationAlarmCheckLocked(mContext, policy);
+ }
+ }
+
+ private void manageMonitoringCertificateNotification(Intent intent) {
+ final NotificationManager notificationManager = getNotificationManager();
+
+ final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
+ if (! hasCert) {
+ if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
+ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ for (UserInfo user : um.getUsers()) {
+ notificationManager.cancelAsUser(
+ null, MONITORING_CERT_NOTIFICATION_ID, user.getUserHandle());
+ }
+ }
+ return;
+ }
+ final boolean isManaged = getDeviceOwner() != null;
+ int smallIconId;
+ String contentText;
+ if (isManaged) {
+ 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;
+ }
+
+ 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");
+ // Notification will be sent individually to all users. The activity should start as
+ // whichever user is current when it starts.
+ PendingIntent notifyIntent = PendingIntent.getActivityAsUser(mContext, 0, dialogIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.CURRENT);
+
+ Notification noti = new Notification.Builder(mContext)
+ .setSmallIcon(smallIconId)
+ .setContentTitle(mContext.getString(R.string.ssl_ca_cert_warning))
+ .setContentText(contentText)
+ .setContentIntent(notifyIntent)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setShowWhen(false)
+ .build();
+
+ // If this is a boot intent, this will fire for each user. But if this is a storage changed
+ // intent, it will fire once, so we need to notify all users.
+ if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
+ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ for (UserInfo user : um.getUsers()) {
+ notificationManager.notifyAsUser(
+ null, MONITORING_CERT_NOTIFICATION_ID, noti, user.getUserHandle());
+ }
+ } else {
+ notificationManager.notifyAsUser(
+ null, MONITORING_CERT_NOTIFICATION_ID, noti, UserHandle.CURRENT);
+ }
+ }
+
+ /**
+ * @param adminReceiver The admin to add
+ * @param refreshing true = update an active admin, no error
+ */
+ public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
+ enforceCrossUserPermission(userHandle);
+
+ DevicePolicyData policy = getUserData(userHandle);
+ DeviceAdminInfo info = findAdmin(adminReceiver, userHandle);
+ if (info == null) {
+ throw new IllegalArgumentException("Bad admin: " + adminReceiver);
+ }
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (!refreshing
+ && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
+ throw new IllegalArgumentException("Admin is already added");
+ }
+ ActiveAdmin newAdmin = new ActiveAdmin(info);
+ policy.mAdminMap.put(adminReceiver, newAdmin);
+ int replaceIndex = -1;
+ if (refreshing) {
+ 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);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public boolean isAdminActive(ComponentName adminReceiver, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
+ }
+ }
+
+ public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (administrator == null) {
+ throw new SecurityException("No active admin " + adminReceiver);
+ }
+ return administrator.info.usesPolicy(policyId);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<ComponentName> getActiveAdmins(int userHandle) {
+ if (!mHasFeature) {
+ return Collections.EMPTY_LIST;
+ }
+
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ if (N <= 0) {
+ return null;
+ }
+ ArrayList<ComponentName> res = new ArrayList<ComponentName>(N);
+ for (int i=0; i<N; i++) {
+ res.add(policy.mAdminList.get(i).info.getComponent());
+ }
+ return res;
+ }
+ }
+
+ public boolean packageHasActiveAdmins(String packageName, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ if (policy.mAdminList.get(i).info.getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ if (admin == null) {
+ return;
+ }
+ if (admin.getUid() != Binder.getCallingUid()) {
+ // If trying to remove device owner, refuse when the caller is not the owner.
+ if (mDeviceOwner != null
+ && adminReceiver.getPackageName().equals(mDeviceOwner.getPackageName())) {
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeActiveAdminLocked(adminReceiver, userHandle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setPasswordQuality(ComponentName who, int quality, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ validateQualityConstant(quality);
+ enforceCrossUserPermission(userHandle);
+
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.passwordQuality != quality) {
+ ap.passwordQuality = quality;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordQuality(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ DevicePolicyData policy = getUserData(userHandle);
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.passwordQuality : mode;
+ }
+
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (mode < admin.passwordQuality) {
+ mode = admin.passwordQuality;
+ }
+ }
+ return mode;
+ }
+ }
+
+ public void setPasswordMinimumLength(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordLength != length) {
+ ap.minimumPasswordLength = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumLength(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordLength : length;
+ }
+
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordLength) {
+ length = admin.minimumPasswordLength;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordHistoryLength(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.passwordHistoryLength != length) {
+ ap.passwordHistoryLength = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordHistoryLength(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.passwordHistoryLength : length;
+ }
+
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.passwordHistoryLength) {
+ length = admin.passwordHistoryLength;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordExpirationTimeout(ComponentName who, long timeout, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("Timeout must be >= 0 ms");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
+ // 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
+ setExpirationAlarmCheckLocked(mContext, getUserData(userHandle));
+ }
+ }
+
+ /**
+ * Return a single admin's expiration cycle time, or the min of all cycle times.
+ * Returns 0 if not configured.
+ */
+ public long getPasswordExpirationTimeout(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0L;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.passwordExpirationTimeout : 0L;
+ }
+
+ long timeout = 0L;
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
+ && timeout > admin.passwordExpirationTimeout)) {
+ timeout = admin.passwordExpirationTimeout;
+ }
+ }
+ return timeout;
+ }
+ }
+
+ /**
+ * 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) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.passwordExpirationDate : 0L;
+ }
+
+ long timeout = 0L;
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (timeout == 0L || (admin.passwordExpirationDate != 0
+ && timeout > admin.passwordExpirationDate)) {
+ timeout = admin.passwordExpirationDate;
+ }
+ }
+ return timeout;
+ }
+
+ public long getPasswordExpiration(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0L;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ return getPasswordExpirationLocked(who, userHandle);
+ }
+ }
+
+ public void setPasswordMinimumUpperCase(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordUpperCase != length) {
+ ap.minimumPasswordUpperCase = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumUpperCase(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordUpperCase : length;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordUpperCase) {
+ length = admin.minimumPasswordUpperCase;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumLowerCase(ComponentName who, int length, int userHandle) {
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordLowerCase != length) {
+ ap.minimumPasswordLowerCase = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumLowerCase(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordLowerCase : length;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordLowerCase) {
+ length = admin.minimumPasswordLowerCase;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumLetters(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordLetters != length) {
+ ap.minimumPasswordLetters = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumLetters(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordLetters : length;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordLetters) {
+ length = admin.minimumPasswordLetters;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumNumeric(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordNumeric != length) {
+ ap.minimumPasswordNumeric = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumNumeric(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordNumeric : length;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordNumeric) {
+ length = admin.minimumPasswordNumeric;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumSymbols(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordSymbols != length) {
+ ap.minimumPasswordSymbols = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumSymbols(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordSymbols : length;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordSymbols) {
+ length = admin.minimumPasswordSymbols;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumNonLetter(ComponentName who, int length, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordNonLetter != length) {
+ ap.minimumPasswordNonLetter = length;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getPasswordMinimumNonLetter(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.minimumPasswordNonLetter : length;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordNonLetter) {
+ length = admin.minimumPasswordNonLetter;
+ }
+ }
+ return length;
+ }
+ }
+
+ public boolean isActivePasswordSufficient(int userHandle) {
+ if (!mHasFeature) {
+ return true;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ // 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);
+ if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle)
+ || policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
+ return false;
+ }
+ if (policy.mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+ return true;
+ }
+ return policy.mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null, userHandle)
+ && policy.mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null, userHandle)
+ && policy.mActivePasswordLetters >= getPasswordMinimumLetters(null, userHandle)
+ && policy.mActivePasswordNumeric >= getPasswordMinimumNumeric(null, userHandle)
+ && policy.mActivePasswordSymbols >= getPasswordMinimumSymbols(null, userHandle)
+ && policy.mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null, userHandle);
+ }
+ }
+
+ public int getCurrentFailedPasswordAttempts(int userHandle) {
+ enforceCrossUserPermission(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_WATCH_LOGIN);
+ return getUserData(userHandle).mFailedPasswordAttempts;
+ }
+ }
+
+ public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(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(who,
+ DeviceAdminInfo.USES_POLICY_WIPE_DATA);
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
+ if (ap.maximumFailedPasswordsForWipe != num) {
+ ap.maximumFailedPasswordsForWipe = num;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ }
+
+ public int getMaximumFailedPasswordsForWipe(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ int count = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.maximumFailedPasswordsForWipe : count;
+ }
+
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (count == 0) {
+ count = admin.maximumFailedPasswordsForWipe;
+ } else if (admin.maximumFailedPasswordsForWipe != 0
+ && count > admin.maximumFailedPasswordsForWipe) {
+ count = admin.maximumFailedPasswordsForWipe;
+ }
+ }
+ return count;
+ }
+ }
+
+ public boolean resetPassword(String password, int flags, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceCrossUserPermission(userHandle);
+ int quality;
+ 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_RESET_PASSWORD);
+ quality = getPasswordQuality(null, userHandle);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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;
+ }
+ }
+ }
+
+ int callingUid = Binder.getCallingUid();
+ 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;
+ }
+
+ // Don't do this with the lock held, because it is going to call
+ // back in to the service.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ utils.saveLockPassword(password, quality, false, userHandle);
+ synchronized (this) {
+ int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
+ != 0 ? callingUid : -1;
+ if (policy.mPasswordOwner != newOwner) {
+ policy.mPasswordOwner = newOwner;
+ saveSettingsLocked(userHandle);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ return true;
+ }
+
+ public void setMaximumTimeToLock(ComponentName who, long timeMs, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
+ if (ap.maximumTimeToUnlock != timeMs) {
+ ap.maximumTimeToUnlock = timeMs;
+ saveSettingsLocked(userHandle);
+ updateMaximumTimeToLockLocked(getUserData(userHandle));
+ }
+ }
+ }
+
+ void updateMaximumTimeToLockLocked(DevicePolicyData policy) {
+ long timeMs = getMaximumTimeToLock(null, policy.mUserHandle);
+ if (policy.mLastMaximumTimeToLock == timeMs) {
+ return;
+ }
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (timeMs <= 0) {
+ timeMs = Integer.MAX_VALUE;
+ } else {
+ // Make sure KEEP_SCREEN_ON is disabled, since that
+ // would allow bypassing of the maximum time to lock.
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+ }
+
+ policy.mLastMaximumTimeToLock = timeMs;
+
+ try {
+ getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
+ } catch (RemoteException e) {
+ Slog.w(LOG_TAG, "Failure talking with power manager", e);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public long getMaximumTimeToLock(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ long time = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return admin != null ? admin.maximumTimeToUnlock : time;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (time == 0) {
+ time = admin.maximumTimeToUnlock;
+ } else if (admin.maximumTimeToUnlock != 0
+ && time > admin.maximumTimeToUnlock) {
+ time = admin.maximumTimeToUnlock;
+ }
+ }
+ return time;
+ }
+ }
+
+ public void lockNow() {
+ 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);
+ lockNowUnchecked();
+ }
+ }
+
+ private void lockNowUnchecked() {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Power off the display
+ getIPowerManager().goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN);
+ // Ensure the device is locked
+ getWindowManager().lockNow(null);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isExtStorageEncrypted() {
+ String state = SystemProperties.get("vold.decrypt");
+ return !"".equals(state);
+ }
+
+ public boolean installCaCert(byte[] certBuffer) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
+ KeyChainConnection keyChainConnection = null;
+ 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;
+ }
+ try {
+ keyChainConnection = KeyChain.bind(mContext);
+ try {
+ keyChainConnection.getService().installCaCertificate(pemCert);
+ return true;
+ } finally {
+ if (keyChainConnection != null) {
+ keyChainConnection.close();
+ keyChainConnection = null;
+ }
+ }
+ } catch (InterruptedException e1) {
+ Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+ Thread.currentThread().interrupt();
+ }
+ return false;
+ }
+
+ private static X509Certificate parseCert(byte[] certBuffer)
+ throws CertificateException, IOException {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(
+ certBuffer));
+ }
+
+ public void uninstallCaCert(final byte[] certBuffer) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
+ TrustedCertificateStore certStore = new TrustedCertificateStore();
+ String alias = null;
+ try {
+ X509Certificate cert = parseCert(certBuffer);
+ alias = certStore.getCertificateAlias(cert);
+ } catch (CertificateException ce) {
+ Log.e(LOG_TAG, "Problem creating X509Certificate", ce);
+ return;
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Problem reading certificate", ioe);
+ return;
+ }
+ try {
+ KeyChainConnection keyChainConnection = KeyChain.bind(mContext);
+ IKeyChainService service = keyChainConnection.getService();
+ try {
+ service.deleteCaCertificate(alias);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
+ } finally {
+ keyChainConnection.close();
+ keyChainConnection = null;
+ }
+ } catch (InterruptedException ie) {
+ Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ void wipeDataLocked(int flags) {
+ // If the SD card is encrypted and non-removable, we have to force a wipe.
+ boolean forceExtWipe = !Environment.isExternalStorageRemovable() && isExtStorageEncrypted();
+ boolean wipeExtRequested = (flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0;
+
+ // Note: we can only do the wipe via ExternalStorageFormatter if the volume is not emulated.
+ if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) {
+ Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
+ intent.putExtra(ExternalStorageFormatter.EXTRA_ALWAYS_RESET, true);
+ intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
+ mWakeLock.acquire(10000);
+ mContext.startService(intent);
+ } else {
+ try {
+ RecoverySystem.rebootWipeUserData(mContext);
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Failed requesting data wipe", e);
+ }
+ }
+ }
+
+ public void wipeData(int flags, final int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(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_WIPE_DATA);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ wipeDeviceOrUserLocked(flags, userHandle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private void wipeDeviceOrUserLocked(int flags, final int userHandle) {
+ if (userHandle == UserHandle.USER_OWNER) {
+ wipeDataLocked(flags);
+ } else {
+ lockNowUnchecked();
+ mHandler.post(new Runnable() {
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
+ ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
+ .removeUser(userHandle);
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ }
+ }
+ });
+ }
+ }
+
+ public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle);
+ if (admin == null) {
+ try {
+ result.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+ Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
+ intent.setComponent(admin.info.getComponent());
+ mContext.sendOrderedBroadcastAsUser(intent, new UserHandle(userHandle),
+ null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ result.sendResult(getResultExtras(false));
+ } catch (RemoteException e) {
+ }
+ }
+ }, null, Activity.RESULT_OK, null, null);
+ }
+ }
+
+ public void setActivePasswordState(int quality, int length, int letters, int uppercase,
+ int lowercase, int numbers, int symbols, int nonletter, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+ DevicePolicyData p = getUserData(userHandle);
+
+ validateQualityConstant(quality);
+
+ synchronized (this) {
+ if (p.mActivePasswordQuality != quality || p.mActivePasswordLength != length
+ || p.mFailedPasswordAttempts != 0 || p.mActivePasswordLetters != letters
+ || p.mActivePasswordUpperCase != uppercase
+ || p.mActivePasswordLowerCase != lowercase
+ || p.mActivePasswordNumeric != numbers
+ || p.mActivePasswordSymbols != symbols
+ || p.mActivePasswordNonLetter != nonletter) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ p.mActivePasswordQuality = quality;
+ p.mActivePasswordLength = length;
+ p.mActivePasswordLetters = letters;
+ p.mActivePasswordLowerCase = lowercase;
+ p.mActivePasswordUpperCase = uppercase;
+ p.mActivePasswordNumeric = numbers;
+ p.mActivePasswordSymbols = symbols;
+ p.mActivePasswordNonLetter = nonletter;
+ p.mFailedPasswordAttempts = 0;
+ saveSettingsLocked(userHandle);
+ updatePasswordExpirationsLocked(userHandle);
+ setExpirationAlarmCheckLocked(mContext, p);
+ sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called any time the device password is updated. Resets all password expiration clocks.
+ */
+ private void updatePasswordExpirationsLocked(int userHandle) {
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ if (N > 0) {
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+ long timeout = admin.passwordExpirationTimeout;
+ long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
+ admin.passwordExpirationDate = expiration;
+ }
+ }
+ saveSettingsLocked(userHandle);
+ }
+ }
+
+ public void reportFailedPasswordAttempt(int userHandle) {
+ enforceCrossUserPermission(userHandle);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(userHandle);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ policy.mFailedPasswordAttempts++;
+ saveSettingsLocked(userHandle);
+ if (mHasFeature) {
+ int max = getMaximumFailedPasswordsForWipe(null, userHandle);
+ if (max > 0 && policy.mFailedPasswordAttempts >= max) {
+ wipeDeviceOrUserLocked(0, userHandle);
+ }
+ sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
+ DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void reportSuccessfulPasswordAttempt(int userHandle) {
+ enforceCrossUserPermission(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 = Binder.clearCallingIdentity();
+ try {
+ policy.mFailedPasswordAttempts = 0;
+ policy.mPasswordOwner = -1;
+ saveSettingsLocked(userHandle);
+ if (mHasFeature) {
+ sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
+ DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+
+ public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
+ String exclusionList, int userHandle) {
+ if (!mHasFeature) {
+ return null;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized(this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+
+ // Only check if owner has set global proxy. We don't allow other users to set it.
+ DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+ 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<ComponentName> 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 the owner, don't set the global proxy. Fail silently.
+ if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
+ Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
+ + userHandle + " 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 = Binder.clearCallingIdentity();
+ resetGlobalProxyLocked(policy);
+ Binder.restoreCallingIdentity(origId);
+ return null;
+ }
+ }
+
+ public ComponentName getGlobalProxyAdmin(int userHandle) {
+ if (!mHasFeature) {
+ return null;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized(this) {
+ DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
+ // 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;
+ }
+
+ 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();
+ ContentResolver res = mContext.getContentResolver();
+
+ ProxyProperties proxyProperties = new ProxyProperties(data[0], proxyPort, exclusionList);
+ if (!proxyProperties.isValid()) {
+ Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
+ return;
+ }
+ Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
+ Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+ Settings.Global.putString(res, 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).
+ */
+ public int setStorageEncryption(ComponentName who, boolean encrypt, int userHandle) {
+ if (!mHasFeature) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ // Check for permissions
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ // Only owner can set storage encryption
+ if (userHandle != UserHandle.USER_OWNER
+ || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
+ Slog.w(LOG_TAG, "Only owner 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_OWNER);
+ // (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.
+ */
+ public boolean getStorageEncryption(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ enforceCrossUserPermission(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.
+ */
+ public int getStorageEncryptionStatus(int userHandle) {
+ if (!mHasFeature) {
+ // Ok to return current status.
+ }
+ enforceCrossUserPermission(userHandle);
+ return getEncryptionStatus();
+ }
+
+ /**
+ * 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} or
+ * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE} or
+ * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
+ */
+ private int getEncryptionStatus() {
+ String status = SystemProperties.get("ro.crypto.state", "unsupported");
+ if ("encrypted".equalsIgnoreCase(status)) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+ } else if ("unencrypted".equalsIgnoreCase(status)) {
+ 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) {
+ }
+
+ /**
+ * The system property used to share the state of the camera. The native camera service
+ * is expected to read this property and act accordingly.
+ */
+ public static final String SYSTEM_PROP_DISABLE_CAMERA = "sys.secpolicy.camera.disabled";
+
+ /**
+ * Disables all device cameras according to the specified admin.
+ */
+ public void setCameraDisabled(ComponentName who, boolean disabled, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA);
+ if (ap.disableCamera != disabled) {
+ ap.disableCamera = disabled;
+ saveSettingsLocked(userHandle);
+ }
+ syncDeviceCapabilitiesLocked(getUserData(userHandle));
+ }
+ }
+
+ /**
+ * Gets whether or not all device cameras are disabled for a given admin, or disabled for any
+ * active admins.
+ */
+ public boolean getCameraDisabled(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return (admin != null) ? admin.disableCamera : false;
+ }
+
+ 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;
+ }
+ }
+
+ /**
+ * Selectively disable keyguard features.
+ */
+ public void setKeyguardDisabledFeatures(ComponentName who, int which, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
+ if (ap.disabledKeyguardFeatures != which) {
+ ap.disabledKeyguardFeatures = which;
+ saveSettingsLocked(userHandle);
+ }
+ syncDeviceCapabilitiesLocked(getUserData(userHandle));
+ }
+ }
+
+ /**
+ * Gets the disabled state for features in keyguard for the given admin,
+ * or the aggregate of all active admins if who is null.
+ */
+ public int getKeyguardDisabledFeatures(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return 0;
+ }
+ enforceCrossUserPermission(userHandle);
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return (admin != null) ? admin.disabledKeyguardFeatures : 0;
+ }
+
+ // Determine which keyguard features are disabled for any active admins.
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ int which = 0;
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ which |= admin.disabledKeyguardFeatures;
+ }
+ return which;
+ }
+ }
+
+ @Override
+ public boolean setDeviceOwner(String packageName, String ownerName) {
+ if (!mHasFeature) {
+ return false;
+ }
+ if (packageName == null
+ || !DeviceOwner.isInstalled(packageName, mContext.getPackageManager())) {
+ throw new IllegalArgumentException("Invalid package name " + packageName
+ + " for device owner");
+ }
+ synchronized (this) {
+ if (mDeviceOwner == null && !isDeviceProvisioned()) {
+ mDeviceOwner = new DeviceOwner(packageName, ownerName);
+ mDeviceOwner.writeOwnerFile();
+ return true;
+ } else {
+ throw new IllegalStateException("Trying to set device owner to " + packageName
+ + ", owner=" + mDeviceOwner.getPackageName()
+ + ", device_provisioned=" + isDeviceProvisioned());
+ }
+ }
+ }
+
+ @Override
+ public boolean isDeviceOwner(String packageName) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ return mDeviceOwner != null
+ && mDeviceOwner.getPackageName().equals(packageName);
+ }
+ }
+
+ @Override
+ public String getDeviceOwner() {
+ if (!mHasFeature) {
+ return null;
+ }
+ synchronized (this) {
+ if (mDeviceOwner != null) {
+ return mDeviceOwner.getPackageName();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getDeviceOwnerName() {
+ if (!mHasFeature) {
+ return null;
+ }
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ synchronized (this) {
+ if (mDeviceOwner != null) {
+ return mDeviceOwner.getName();
+ }
+ }
+ return null;
+ }
+
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) > 0;
+ }
+
+ private void enforceCrossUserPermission(int userHandle) {
+ if (userHandle < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userHandle);
+ }
+ final int callingUid = Binder.getCallingUid();
+ if (userHandle == UserHandle.getUserId(callingUid)) return;
+ if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
+ + " INTERACT_ACROSS_USERS_FULL permission");
+ }
+ }
+
+ private void enableIfNecessary(String packageName, int userId) {
+ try {
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ ApplicationInfo ai = ipm.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ userId);
+ if (ai.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ ipm.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="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final Printer p = new PrintWriterPrinter(pw);
+
+ synchronized (this) {
+ p.println("Current Device Policy Manager state:");
+
+ int userCount = mUserData.size();
+ for (int u = 0; u < userCount; u++) {
+ DevicePolicyData policy = getUserData(mUserData.keyAt(u));
+ p.println(" Enabled Device Admins (User " + policy.mUserHandle + "):");
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin ap = policy.mAdminList.get(i);
+ if (ap != null) {
+ pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString());
+ pw.println(":");
+ ap.dump(" ", pw);
+ }
+ }
+
+ pw.println(" ");
+ pw.print(" mPasswordOwner="); pw.println(policy.mPasswordOwner);
+ }
+ }
+ }
+
+ static class DeviceOwner {
+ private static final String DEVICE_OWNER_XML = "device_owner.xml";
+ private static final String TAG_DEVICE_OWNER = "device-owner";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_PACKAGE = "package";
+ private String mPackageName;
+ private String mOwnerName;
+
+ DeviceOwner() {
+ readOwnerFile();
+ }
+
+ DeviceOwner(String packageName, String ownerName) {
+ this.mPackageName = packageName;
+ this.mOwnerName = ownerName;
+ }
+
+ static boolean isRegistered() {
+ return new File(Environment.getSystemSecureDirectory(),
+ DEVICE_OWNER_XML).exists();
+ }
+
+ String getPackageName() {
+ return mPackageName;
+ }
+
+ String getName() {
+ return mOwnerName;
+ }
+
+ static boolean isInstalled(String packageName, PackageManager pm) {
+ try {
+ PackageInfo pi;
+ if ((pi = pm.getPackageInfo(packageName, 0)) != null) {
+ if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return true;
+ }
+ }
+ } catch (NameNotFoundException nnfe) {
+ Slog.w(LOG_TAG, "Device Owner package " + packageName + " not installed.");
+ }
+ return false;
+ }
+
+ void readOwnerFile() {
+ AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
+ DEVICE_OWNER_XML));
+ try {
+ FileInputStream input = file.openRead();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(input, null);
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+ String tag = parser.getName();
+ if (!TAG_DEVICE_OWNER.equals(tag)) {
+ throw new XmlPullParserException(
+ "Device Owner file does not start with device-owner tag: found " + tag);
+ }
+ mPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+ mOwnerName = parser.getAttributeValue(null, ATTR_NAME);
+ input.close();
+ } catch (XmlPullParserException xppe) {
+ Slog.e(LOG_TAG, "Error parsing device-owner file\n" + xppe);
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "IO Exception when reading device-owner file\n" + ioe);
+ }
+ }
+
+ void writeOwnerFile() {
+ synchronized (this) {
+ writeOwnerFileLocked();
+ }
+ }
+
+ private void writeOwnerFileLocked() {
+ AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
+ DEVICE_OWNER_XML));
+ try {
+ FileOutputStream output = file.startWrite();
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(output, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_DEVICE_OWNER);
+ out.attribute(null, ATTR_PACKAGE, mPackageName);
+ if (mOwnerName != null) {
+ out.attribute(null, ATTR_NAME, mOwnerName);
+ }
+ out.endTag(null, TAG_DEVICE_OWNER);
+ out.endDocument();
+ out.flush();
+ file.finishWrite(output);
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "IO Exception when writing device-owner file\n" + ioe);
+ }
+ }
+ }
+}
diff --git a/services/devicepolicy/java/service.mk b/services/devicepolicy/java/service.mk
new file mode 100644
index 0000000..e64cc96
--- /dev/null
+++ b/services/devicepolicy/java/service.mk
@@ -0,0 +1,11 @@
+# Include only if the service is required
+ifneq ($(findstring devicepolicy,$(REQUIRED_SERVICES)),)
+
+SUB_DIR := devicepolicy/java
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,$(SUB_DIR))
+
+#DEFINED_SERVICES += com.android.server.devicepolicy.DevicePolicyManagerService
+
+endif