blob: 36da4eb3d65c4b93b67b7a9ce2990ea8c477abac [file] [log] [blame]
/*
* 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);
}
}
}
}
}