544 lines
20 KiB
Java
544 lines
20 KiB
Java
/*
|
|
* 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;
|
|
|
|
import com.android.common.FastXmlSerializer;
|
|
import com.android.internal.widget.LockPatternUtils;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
import org.xmlpull.v1.XmlSerializer;
|
|
|
|
import android.app.DeviceAdmin;
|
|
import android.app.DeviceAdminInfo;
|
|
import android.app.DevicePolicyManager;
|
|
import android.app.IDevicePolicyManager;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.os.Binder;
|
|
import android.os.IBinder;
|
|
import android.os.IPowerManager;
|
|
import android.os.RecoverySystem;
|
|
import android.os.RemoteException;
|
|
import android.os.ServiceManager;
|
|
import android.util.Log;
|
|
import android.util.Xml;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Implementation of the device policy APIs.
|
|
*/
|
|
public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
|
private static final String TAG = "DevicePolicyManagerService";
|
|
|
|
private final Context mContext;
|
|
|
|
IPowerManager mIPowerManager;
|
|
|
|
int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
|
|
int mActivePasswordLength = 0;
|
|
int mFailedPasswordAttempts = 0;
|
|
|
|
ActiveAdmin mActiveAdmin;
|
|
|
|
static class ActiveAdmin {
|
|
ActiveAdmin(DeviceAdminInfo _info) {
|
|
info = _info;
|
|
}
|
|
|
|
final DeviceAdminInfo info;
|
|
int getUid() { return info.getActivityInfo().applicationInfo.uid; }
|
|
|
|
int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
|
|
int minimumPasswordLength = 0;
|
|
long maximumTimeToUnlock = 0;
|
|
}
|
|
|
|
/**
|
|
* Instantiates the service.
|
|
*/
|
|
public DevicePolicyManagerService(Context context) {
|
|
mContext = context;
|
|
}
|
|
|
|
private IPowerManager getIPowerManager() {
|
|
if (mIPowerManager == null) {
|
|
IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
|
|
mIPowerManager = IPowerManager.Stub.asInterface(b);
|
|
}
|
|
return mIPowerManager;
|
|
}
|
|
|
|
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
|
|
if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) {
|
|
if (who != null) {
|
|
if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
|
|
|| !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
|
|
throw new SecurityException("Current admin is not " + who);
|
|
}
|
|
}
|
|
return mActiveAdmin;
|
|
}
|
|
throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
|
|
}
|
|
|
|
|
|
void sendAdminCommandLocked(ActiveAdmin policy, String action) {
|
|
Intent intent = new Intent(action);
|
|
intent.setComponent(policy.info.getComponent());
|
|
mContext.sendBroadcast(intent);
|
|
}
|
|
|
|
void sendAdminCommandLocked(String action) {
|
|
if (mActiveAdmin != null) {
|
|
sendAdminCommandLocked(mActiveAdmin, action);
|
|
}
|
|
}
|
|
|
|
ComponentName getActiveAdminLocked() {
|
|
if (mActiveAdmin != null) {
|
|
return mActiveAdmin.info.getComponent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void removeActiveAdminLocked(ComponentName adminReceiver) {
|
|
ComponentName cur = getActiveAdminLocked();
|
|
if (cur != null && cur.equals(adminReceiver)) {
|
|
sendAdminCommandLocked(mActiveAdmin,
|
|
DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
|
|
// XXX need to wait for it to complete.
|
|
mActiveAdmin = null;
|
|
}
|
|
}
|
|
|
|
public DeviceAdminInfo findAdmin(ComponentName adminName) {
|
|
Intent resolveIntent = new Intent();
|
|
resolveIntent.setComponent(adminName);
|
|
List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
|
|
resolveIntent, PackageManager.GET_META_DATA);
|
|
if (infos == null || infos.size() <= 0) {
|
|
throw new IllegalArgumentException("Unknown admin: " + adminName);
|
|
}
|
|
|
|
try {
|
|
return new DeviceAdminInfo(mContext, infos.get(0));
|
|
} catch (XmlPullParserException e) {
|
|
Log.w(TAG, "Bad device admin requested: " + adminName, e);
|
|
return null;
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "Bad device admin requested: " + adminName, e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static JournaledFile makeJournaledFile() {
|
|
final String base = "/data/system/device_policies.xml";
|
|
return new JournaledFile(new File(base), new File(base + ".tmp"));
|
|
}
|
|
|
|
private void saveSettingsLocked() {
|
|
JournaledFile journal = makeJournaledFile();
|
|
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");
|
|
|
|
ActiveAdmin ap = mActiveAdmin;
|
|
if (ap != null) {
|
|
out.startTag(null, "admin");
|
|
out.attribute(null, "name", ap.info.getComponent().flattenToString());
|
|
if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
|
|
out.startTag(null, "password-mode");
|
|
out.attribute(null, "value", Integer.toString(ap.passwordMode));
|
|
out.endTag(null, "password-mode");
|
|
if (ap.minimumPasswordLength > 0) {
|
|
out.startTag(null, "min-password-length");
|
|
out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
|
|
out.endTag(null, "mn-password-length");
|
|
}
|
|
}
|
|
if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
|
|
out.startTag(null, "max-time-to-unlock");
|
|
out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
|
|
out.endTag(null, "max-time-to-unlock");
|
|
}
|
|
out.endTag(null, "admin");
|
|
}
|
|
out.endTag(null, "policies");
|
|
|
|
out.endDocument();
|
|
stream.close();
|
|
journal.commit();
|
|
} catch (IOException e) {
|
|
try {
|
|
if (stream != null) {
|
|
stream.close();
|
|
}
|
|
} catch (IOException ex) {
|
|
// Ignore
|
|
}
|
|
journal.rollback();
|
|
}
|
|
}
|
|
|
|
private void loadSettingsLocked() {
|
|
JournaledFile journal = makeJournaledFile();
|
|
FileInputStream stream = null;
|
|
File file = journal.chooseForRead();
|
|
boolean success = false;
|
|
try {
|
|
stream = new FileInputStream(file);
|
|
XmlPullParser parser = Xml.newPullParser();
|
|
parser.setInput(stream, null);
|
|
|
|
int type = parser.next();
|
|
while (type != XmlPullParser.START_TAG) {
|
|
type = parser.next();
|
|
}
|
|
String tag = parser.getName();
|
|
if ("policies".equals(tag)) {
|
|
ActiveAdmin ap = null;
|
|
do {
|
|
type = parser.next();
|
|
if (type == XmlPullParser.START_TAG) {
|
|
tag = parser.getName();
|
|
if (ap == null) {
|
|
if ("admin".equals(tag)) {
|
|
DeviceAdminInfo dai = findAdmin(
|
|
ComponentName.unflattenFromString(
|
|
parser.getAttributeValue(null, "name")));
|
|
if (dai != null) {
|
|
ap = new ActiveAdmin(dai);
|
|
}
|
|
}
|
|
} else if ("password-mode".equals(tag)) {
|
|
ap.passwordMode = Integer.parseInt(
|
|
parser.getAttributeValue(null, "value"));
|
|
} else if ("min-password-length".equals(tag)) {
|
|
ap.minimumPasswordLength = Integer.parseInt(
|
|
parser.getAttributeValue(null, "value"));
|
|
} else if ("max-time-to-unlock".equals(tag)) {
|
|
ap.maximumTimeToUnlock = Long.parseLong(
|
|
parser.getAttributeValue(null, "value"));
|
|
}
|
|
} else if (type == XmlPullParser.END_TAG) {
|
|
tag = parser.getName();
|
|
if (ap != null && "admin".equals(tag)) {
|
|
mActiveAdmin = ap;
|
|
ap = null;
|
|
}
|
|
}
|
|
} while (type != XmlPullParser.END_DOCUMENT);
|
|
success = true;
|
|
}
|
|
} catch (NullPointerException e) {
|
|
Log.w(TAG, "failed parsing " + file + " " + e);
|
|
} catch (NumberFormatException e) {
|
|
Log.w(TAG, "failed parsing " + file + " " + e);
|
|
} catch (XmlPullParserException e) {
|
|
Log.w(TAG, "failed parsing " + file + " " + e);
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "failed parsing " + file + " " + e);
|
|
} catch (IndexOutOfBoundsException e) {
|
|
Log.w(TAG, "failed parsing " + file + " " + e);
|
|
}
|
|
try {
|
|
if (stream != null) {
|
|
stream.close();
|
|
}
|
|
} catch (IOException e) {
|
|
// Ignore
|
|
}
|
|
|
|
if (!success) {
|
|
Log.w(TAG, "No valid start tag found in policies file");
|
|
}
|
|
|
|
long timeMs = getMaximumTimeToLock();
|
|
if (timeMs <= 0) {
|
|
timeMs = Integer.MAX_VALUE;
|
|
}
|
|
try {
|
|
getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Failure talking with power manager", e);
|
|
}
|
|
|
|
}
|
|
|
|
public void systemReady() {
|
|
synchronized (this) {
|
|
loadSettingsLocked();
|
|
}
|
|
}
|
|
|
|
public void setActiveAdmin(ComponentName adminReceiver) {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
|
|
|
|
DeviceAdminInfo info = findAdmin(adminReceiver);
|
|
if (info == null) {
|
|
throw new IllegalArgumentException("Bad admin: " + adminReceiver);
|
|
}
|
|
synchronized (this) {
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
ComponentName cur = getActiveAdminLocked();
|
|
if (cur != null && cur.equals(adminReceiver)) {
|
|
throw new IllegalStateException("An admin is already set");
|
|
}
|
|
if (cur != null) {
|
|
removeActiveAdminLocked(adminReceiver);
|
|
}
|
|
mActiveAdmin = new ActiveAdmin(info);
|
|
saveSettingsLocked();
|
|
sendAdminCommandLocked(mActiveAdmin,
|
|
DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
public ComponentName getActiveAdmin() {
|
|
synchronized (this) {
|
|
return getActiveAdminLocked();
|
|
}
|
|
}
|
|
|
|
public void removeActiveAdmin(ComponentName adminReceiver) {
|
|
synchronized (this) {
|
|
if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
|
|
}
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
removeActiveAdminLocked(adminReceiver);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setPasswordMode(ComponentName who, int mode) {
|
|
synchronized (this) {
|
|
if (who == null) {
|
|
throw new NullPointerException("ComponentName is null");
|
|
}
|
|
ActiveAdmin ap = getActiveAdminForCallerLocked(who);
|
|
if (ap.passwordMode != mode) {
|
|
ap.passwordMode = mode;
|
|
saveSettingsLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getPasswordMode() {
|
|
synchronized (this) {
|
|
return mActiveAdmin != null ? mActiveAdmin.passwordMode
|
|
: DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
|
|
}
|
|
}
|
|
|
|
public void setMinimumPasswordLength(ComponentName who, int length) {
|
|
synchronized (this) {
|
|
if (who == null) {
|
|
throw new NullPointerException("ComponentName is null");
|
|
}
|
|
ActiveAdmin ap = getActiveAdminForCallerLocked(who);
|
|
if (ap.minimumPasswordLength != length) {
|
|
ap.minimumPasswordLength = length;
|
|
saveSettingsLocked();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getMinimumPasswordLength() {
|
|
synchronized (this) {
|
|
return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
|
|
}
|
|
}
|
|
|
|
public boolean isActivePasswordSufficient() {
|
|
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);
|
|
return mActivePasswordMode >= getPasswordMode()
|
|
&& mActivePasswordLength >= getMinimumPasswordLength();
|
|
}
|
|
}
|
|
|
|
public int getCurrentFailedPasswordAttempts() {
|
|
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);
|
|
return mFailedPasswordAttempts;
|
|
}
|
|
}
|
|
|
|
public boolean resetPassword(String password) {
|
|
int mode;
|
|
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);
|
|
mode = getPasswordMode();
|
|
if (password.length() < getMinimumPasswordLength()) {
|
|
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, mode);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void setMaximumTimeToLock(ComponentName who, long timeMs) {
|
|
synchronized (this) {
|
|
if (who == null) {
|
|
throw new NullPointerException("ComponentName is null");
|
|
}
|
|
ActiveAdmin ap = getActiveAdminForCallerLocked(who);
|
|
if (ap.maximumTimeToUnlock != timeMs) {
|
|
ap.maximumTimeToUnlock = timeMs;
|
|
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
saveSettingsLocked();
|
|
if (timeMs <= 0) {
|
|
timeMs = Integer.MAX_VALUE;
|
|
}
|
|
try {
|
|
getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Failure talking with power manager", e);
|
|
}
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public long getMaximumTimeToLock() {
|
|
synchronized (this) {
|
|
return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
|
|
}
|
|
}
|
|
|
|
public void lockNow() {
|
|
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);
|
|
// STOPSHIP need to implement.
|
|
}
|
|
}
|
|
|
|
public void wipeData(int flags) {
|
|
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);
|
|
}
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
RecoverySystem.rebootWipeUserData(mContext);
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "Failed requesting data wipe", e);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
public void setActivePasswordState(int mode, int length) {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
|
|
|
|
synchronized (this) {
|
|
if (mActivePasswordMode != mode || mActivePasswordLength != length
|
|
|| mFailedPasswordAttempts != 0) {
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mActivePasswordMode = mode;
|
|
mActivePasswordLength = length;
|
|
mFailedPasswordAttempts = 0;
|
|
sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void reportFailedPasswordAttempt() {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
|
|
|
|
synchronized (this) {
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mFailedPasswordAttempts++;
|
|
sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void reportSuccessfulPasswordAttempt() {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
|
|
|
|
synchronized (this) {
|
|
if (mFailedPasswordAttempts != 0) {
|
|
long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mFailedPasswordAttempts = 0;
|
|
sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|