blob: 36da4eb3d65c4b93b67b7a9ce2990ea8c477abac [file] [log] [blame]
Dianne Hackbornd6847842010-01-12 18:14:19 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import com.android.common.FastXmlSerializer;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080020import com.android.internal.widget.LockPatternUtils;
Dianne Hackbornd6847842010-01-12 18:14:19 -080021
22import org.xmlpull.v1.XmlPullParser;
23import org.xmlpull.v1.XmlPullParserException;
24import org.xmlpull.v1.XmlSerializer;
25
26import android.app.DeviceAdmin;
27import android.app.DeviceAdminInfo;
28import android.app.DevicePolicyManager;
29import android.app.IDevicePolicyManager;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.os.Binder;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080036import android.os.IBinder;
37import android.os.IPowerManager;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080038import android.os.RecoverySystem;
39import android.os.RemoteException;
40import android.os.ServiceManager;
Dianne Hackbornd6847842010-01-12 18:14:19 -080041import android.util.Log;
42import android.util.Xml;
43
44import java.io.File;
45import java.io.FileInputStream;
46import java.io.FileOutputStream;
47import java.io.IOException;
48import java.util.List;
49
50/**
51 * Implementation of the device policy APIs.
52 */
53public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
54 private static final String TAG = "DevicePolicyManagerService";
55
56 private final Context mContext;
57
Dianne Hackborndf83afa2010-01-20 13:37:26 -080058 IPowerManager mIPowerManager;
59
Dianne Hackbornd6847842010-01-12 18:14:19 -080060 int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
61 int mActivePasswordLength = 0;
62 int mFailedPasswordAttempts = 0;
63
64 ActiveAdmin mActiveAdmin;
65
66 static class ActiveAdmin {
67 ActiveAdmin(DeviceAdminInfo _info) {
68 info = _info;
69 }
70
71 final DeviceAdminInfo info;
72 int getUid() { return info.getActivityInfo().applicationInfo.uid; }
73
74 int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
75 int minimumPasswordLength = 0;
76 long maximumTimeToUnlock = 0;
77 }
78
79 /**
80 * Instantiates the service.
81 */
82 public DevicePolicyManagerService(Context context) {
83 mContext = context;
84 }
85
Dianne Hackborndf83afa2010-01-20 13:37:26 -080086 private IPowerManager getIPowerManager() {
87 if (mIPowerManager == null) {
88 IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
89 mIPowerManager = IPowerManager.Stub.asInterface(b);
90 }
91 return mIPowerManager;
92 }
93
Dianne Hackbornd6847842010-01-12 18:14:19 -080094 ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
Dianne Hackborndf83afa2010-01-20 13:37:26 -080095 if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) {
Dianne Hackbornd6847842010-01-12 18:14:19 -080096 if (who != null) {
97 if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
98 || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
99 throw new SecurityException("Current admin is not " + who);
100 }
101 }
102 return mActiveAdmin;
103 }
104 throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
105 }
106
107
108 void sendAdminCommandLocked(ActiveAdmin policy, String action) {
109 Intent intent = new Intent(action);
110 intent.setComponent(policy.info.getComponent());
111 mContext.sendBroadcast(intent);
112 }
113
Dianne Hackborn4141d032010-01-21 16:29:00 -0800114 void sendAdminCommandLocked(String action) {
115 if (mActiveAdmin != null) {
116 sendAdminCommandLocked(mActiveAdmin, action);
117 }
118 }
119
Dianne Hackbornd6847842010-01-12 18:14:19 -0800120 ComponentName getActiveAdminLocked() {
121 if (mActiveAdmin != null) {
122 return mActiveAdmin.info.getComponent();
123 }
124 return null;
125 }
126
127 void removeActiveAdminLocked(ComponentName adminReceiver) {
128 ComponentName cur = getActiveAdminLocked();
129 if (cur != null && cur.equals(adminReceiver)) {
130 sendAdminCommandLocked(mActiveAdmin,
131 DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
132 // XXX need to wait for it to complete.
133 mActiveAdmin = null;
134 }
135 }
136
137 public DeviceAdminInfo findAdmin(ComponentName adminName) {
138 Intent resolveIntent = new Intent();
139 resolveIntent.setComponent(adminName);
140 List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
141 resolveIntent, PackageManager.GET_META_DATA);
142 if (infos == null || infos.size() <= 0) {
143 throw new IllegalArgumentException("Unknown admin: " + adminName);
144 }
145
146 try {
147 return new DeviceAdminInfo(mContext, infos.get(0));
148 } catch (XmlPullParserException e) {
149 Log.w(TAG, "Bad device admin requested: " + adminName, e);
150 return null;
151 } catch (IOException e) {
152 Log.w(TAG, "Bad device admin requested: " + adminName, e);
153 return null;
154 }
155 }
156
157 private static JournaledFile makeJournaledFile() {
158 final String base = "/data/system/device_policies.xml";
159 return new JournaledFile(new File(base), new File(base + ".tmp"));
160 }
161
162 private void saveSettingsLocked() {
163 JournaledFile journal = makeJournaledFile();
164 FileOutputStream stream = null;
165 try {
166 stream = new FileOutputStream(journal.chooseForWrite(), false);
167 XmlSerializer out = new FastXmlSerializer();
168 out.setOutput(stream, "utf-8");
169 out.startDocument(null, true);
170
171 out.startTag(null, "policies");
172
173 ActiveAdmin ap = mActiveAdmin;
174 if (ap != null) {
175 out.startTag(null, "admin");
176 out.attribute(null, "name", ap.info.getComponent().flattenToString());
177 if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
178 out.startTag(null, "password-mode");
179 out.attribute(null, "value", Integer.toString(ap.passwordMode));
180 out.endTag(null, "password-mode");
181 if (ap.minimumPasswordLength > 0) {
182 out.startTag(null, "min-password-length");
183 out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
184 out.endTag(null, "mn-password-length");
185 }
186 }
187 if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
188 out.startTag(null, "max-time-to-unlock");
189 out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
190 out.endTag(null, "max-time-to-unlock");
191 }
192 out.endTag(null, "admin");
193 }
194 out.endTag(null, "policies");
195
196 out.endDocument();
197 stream.close();
198 journal.commit();
199 } catch (IOException e) {
200 try {
201 if (stream != null) {
202 stream.close();
203 }
204 } catch (IOException ex) {
205 // Ignore
206 }
207 journal.rollback();
208 }
209 }
210
211 private void loadSettingsLocked() {
212 JournaledFile journal = makeJournaledFile();
213 FileInputStream stream = null;
214 File file = journal.chooseForRead();
215 boolean success = false;
216 try {
217 stream = new FileInputStream(file);
218 XmlPullParser parser = Xml.newPullParser();
219 parser.setInput(stream, null);
220
221 int type = parser.next();
222 while (type != XmlPullParser.START_TAG) {
223 type = parser.next();
224 }
225 String tag = parser.getName();
226 if ("policies".equals(tag)) {
227 ActiveAdmin ap = null;
228 do {
229 type = parser.next();
230 if (type == XmlPullParser.START_TAG) {
231 tag = parser.getName();
232 if (ap == null) {
233 if ("admin".equals(tag)) {
234 DeviceAdminInfo dai = findAdmin(
235 ComponentName.unflattenFromString(
236 parser.getAttributeValue(null, "name")));
237 if (dai != null) {
238 ap = new ActiveAdmin(dai);
239 }
240 }
241 } else if ("password-mode".equals(tag)) {
242 ap.passwordMode = Integer.parseInt(
243 parser.getAttributeValue(null, "value"));
244 } else if ("min-password-length".equals(tag)) {
245 ap.minimumPasswordLength = Integer.parseInt(
246 parser.getAttributeValue(null, "value"));
247 } else if ("max-time-to-unlock".equals(tag)) {
248 ap.maximumTimeToUnlock = Long.parseLong(
249 parser.getAttributeValue(null, "value"));
250 }
251 } else if (type == XmlPullParser.END_TAG) {
252 tag = parser.getName();
253 if (ap != null && "admin".equals(tag)) {
254 mActiveAdmin = ap;
255 ap = null;
256 }
257 }
258 } while (type != XmlPullParser.END_DOCUMENT);
259 success = true;
260 }
261 } catch (NullPointerException e) {
262 Log.w(TAG, "failed parsing " + file + " " + e);
263 } catch (NumberFormatException e) {
264 Log.w(TAG, "failed parsing " + file + " " + e);
265 } catch (XmlPullParserException e) {
266 Log.w(TAG, "failed parsing " + file + " " + e);
267 } catch (IOException e) {
268 Log.w(TAG, "failed parsing " + file + " " + e);
269 } catch (IndexOutOfBoundsException e) {
270 Log.w(TAG, "failed parsing " + file + " " + e);
271 }
272 try {
273 if (stream != null) {
274 stream.close();
275 }
276 } catch (IOException e) {
277 // Ignore
278 }
279
280 if (!success) {
281 Log.w(TAG, "No valid start tag found in policies file");
282 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800283
284 long timeMs = getMaximumTimeToLock();
285 if (timeMs <= 0) {
286 timeMs = Integer.MAX_VALUE;
287 }
288 try {
289 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
290 } catch (RemoteException e) {
291 Log.w(TAG, "Failure talking with power manager", e);
292 }
293
Dianne Hackbornd6847842010-01-12 18:14:19 -0800294 }
295
296 public void systemReady() {
297 synchronized (this) {
298 loadSettingsLocked();
299 }
300 }
301
302 public void setActiveAdmin(ComponentName adminReceiver) {
303 mContext.enforceCallingOrSelfPermission(
304 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
305
306 DeviceAdminInfo info = findAdmin(adminReceiver);
307 if (info == null) {
308 throw new IllegalArgumentException("Bad admin: " + adminReceiver);
309 }
310 synchronized (this) {
311 long ident = Binder.clearCallingIdentity();
312 try {
313 ComponentName cur = getActiveAdminLocked();
314 if (cur != null && cur.equals(adminReceiver)) {
315 throw new IllegalStateException("An admin is already set");
316 }
317 if (cur != null) {
318 removeActiveAdminLocked(adminReceiver);
319 }
320 mActiveAdmin = new ActiveAdmin(info);
321 saveSettingsLocked();
322 sendAdminCommandLocked(mActiveAdmin,
323 DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
324 } finally {
325 Binder.restoreCallingIdentity(ident);
326 }
327 }
328 }
329
330 public ComponentName getActiveAdmin() {
331 synchronized (this) {
332 return getActiveAdminLocked();
333 }
334 }
335
336 public void removeActiveAdmin(ComponentName adminReceiver) {
337 synchronized (this) {
338 if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
339 mContext.enforceCallingOrSelfPermission(
340 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
341 }
342 long ident = Binder.clearCallingIdentity();
343 try {
344 removeActiveAdminLocked(adminReceiver);
345 } finally {
346 Binder.restoreCallingIdentity(ident);
347 }
348 }
349 }
350
351 public void setPasswordMode(ComponentName who, int mode) {
352 synchronized (this) {
353 if (who == null) {
354 throw new NullPointerException("ComponentName is null");
355 }
356 ActiveAdmin ap = getActiveAdminForCallerLocked(who);
357 if (ap.passwordMode != mode) {
358 ap.passwordMode = mode;
359 saveSettingsLocked();
360 }
361 }
362 }
363
364 public int getPasswordMode() {
365 synchronized (this) {
366 return mActiveAdmin != null ? mActiveAdmin.passwordMode
367 : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
368 }
369 }
370
Dianne Hackbornd6847842010-01-12 18:14:19 -0800371 public void setMinimumPasswordLength(ComponentName who, int length) {
372 synchronized (this) {
373 if (who == null) {
374 throw new NullPointerException("ComponentName is null");
375 }
376 ActiveAdmin ap = getActiveAdminForCallerLocked(who);
377 if (ap.minimumPasswordLength != length) {
378 ap.minimumPasswordLength = length;
379 saveSettingsLocked();
380 }
381 }
382 }
383
384 public int getMinimumPasswordLength() {
385 synchronized (this) {
386 return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
387 }
388 }
389
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800390 public boolean isActivePasswordSufficient() {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800391 synchronized (this) {
392 // This API can only be called by an active device admin,
393 // so try to retrieve it to check that the caller is one.
394 getActiveAdminForCallerLocked(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800395 return mActivePasswordMode >= getPasswordMode()
396 && mActivePasswordLength >= getMinimumPasswordLength();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800397 }
398 }
399
400 public int getCurrentFailedPasswordAttempts() {
401 synchronized (this) {
402 // This API can only be called by an active device admin,
403 // so try to retrieve it to check that the caller is one.
404 getActiveAdminForCallerLocked(null);
405 return mFailedPasswordAttempts;
406 }
407 }
408
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800409 public boolean resetPassword(String password) {
410 int mode;
411 synchronized (this) {
412 // This API can only be called by an active device admin,
413 // so try to retrieve it to check that the caller is one.
414 getActiveAdminForCallerLocked(null);
415 mode = getPasswordMode();
416 if (password.length() < getMinimumPasswordLength()) {
417 return false;
418 }
419 }
420
421 // Don't do this with the lock held, because it is going to call
422 // back in to the service.
423 long ident = Binder.clearCallingIdentity();
424 try {
425 LockPatternUtils utils = new LockPatternUtils(mContext);
426 utils.saveLockPassword(password, mode);
427 } finally {
428 Binder.restoreCallingIdentity(ident);
429 }
430
431 return true;
432 }
433
Dianne Hackbornd6847842010-01-12 18:14:19 -0800434 public void setMaximumTimeToLock(ComponentName who, long timeMs) {
435 synchronized (this) {
436 if (who == null) {
437 throw new NullPointerException("ComponentName is null");
438 }
439 ActiveAdmin ap = getActiveAdminForCallerLocked(who);
440 if (ap.maximumTimeToUnlock != timeMs) {
441 ap.maximumTimeToUnlock = timeMs;
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800442
443 long ident = Binder.clearCallingIdentity();
444 try {
445 saveSettingsLocked();
446 if (timeMs <= 0) {
447 timeMs = Integer.MAX_VALUE;
448 }
449 try {
450 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
451 } catch (RemoteException e) {
452 Log.w(TAG, "Failure talking with power manager", e);
453 }
454 } finally {
455 Binder.restoreCallingIdentity(ident);
456 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800457 }
458 }
459 }
460
461 public long getMaximumTimeToLock() {
462 synchronized (this) {
463 return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
464 }
465 }
466
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800467 public void lockNow() {
468 synchronized (this) {
469 // This API can only be called by an active device admin,
470 // so try to retrieve it to check that the caller is one.
471 getActiveAdminForCallerLocked(null);
472 // STOPSHIP need to implement.
473 }
474 }
475
Dianne Hackbornd6847842010-01-12 18:14:19 -0800476 public void wipeData(int flags) {
477 synchronized (this) {
478 // This API can only be called by an active device admin,
479 // so try to retrieve it to check that the caller is one.
480 getActiveAdminForCallerLocked(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800481 }
482 long ident = Binder.clearCallingIdentity();
483 try {
484 RecoverySystem.rebootWipeUserData(mContext);
485 } catch (IOException e) {
486 Log.w(TAG, "Failed requesting data wipe", e);
487 } finally {
488 Binder.restoreCallingIdentity(ident);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800489 }
490 }
491
492 public void setActivePasswordState(int mode, int length) {
493 mContext.enforceCallingOrSelfPermission(
494 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
495
496 synchronized (this) {
497 if (mActivePasswordMode != mode || mActivePasswordLength != length
498 || mFailedPasswordAttempts != 0) {
499 long ident = Binder.clearCallingIdentity();
500 try {
501 mActivePasswordMode = mode;
502 mActivePasswordLength = length;
503 mFailedPasswordAttempts = 0;
Dianne Hackborn4141d032010-01-21 16:29:00 -0800504 sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800505 } finally {
506 Binder.restoreCallingIdentity(ident);
507 }
508 }
509 }
510 }
511
512 public void reportFailedPasswordAttempt() {
513 mContext.enforceCallingOrSelfPermission(
514 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
515
516 synchronized (this) {
517 long ident = Binder.clearCallingIdentity();
518 try {
519 mFailedPasswordAttempts++;
Dianne Hackborn4141d032010-01-21 16:29:00 -0800520 sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800521 } finally {
522 Binder.restoreCallingIdentity(ident);
523 }
524 }
525 }
526
527 public void reportSuccessfulPasswordAttempt() {
528 mContext.enforceCallingOrSelfPermission(
529 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
530
531 synchronized (this) {
532 if (mFailedPasswordAttempts != 0) {
533 long ident = Binder.clearCallingIdentity();
534 try {
535 mFailedPasswordAttempts = 0;
Dianne Hackborn4141d032010-01-21 16:29:00 -0800536 sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800537 } finally {
538 Binder.restoreCallingIdentity(ident);
539 }
540 }
541 }
542 }
543}