diff options
author | Amith Yamasani <yamasani@google.com> | 2013-12-19 23:30:35 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-12-19 23:30:35 +0000 |
commit | 49782e46c0eb85a25ae2abcf80880c48dbab5aea (patch) | |
tree | 9fab2a40c41004d78b7001dc766d85f61d24f582 /services/devicepolicy | |
parent | 4dace6f66d498c2d119adf265776aa83b28452af (diff) | |
parent | 9158825f9c41869689d6b1786d7c7aa8bdd524ce (diff) | |
download | frameworks_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.java | 2960 | ||||
-rw-r--r-- | services/devicepolicy/java/service.mk | 11 |
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 |