blob: fbd53173fd0be74ef161b28881f0e67d8aafb557 [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 Hackborn8aa2e892010-01-22 11:31:30 -080094 ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
95 throws SecurityException {
Dianne Hackborndf83afa2010-01-20 13:37:26 -080096 if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) {
Dianne Hackbornd6847842010-01-12 18:14:19 -080097 if (who != null) {
98 if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
99 || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
100 throw new SecurityException("Current admin is not " + who);
101 }
102 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800103 if (!mActiveAdmin.info.usesPolicy(reqPolicy)) {
104 throw new SecurityException("Admin " + mActiveAdmin.info.getComponent()
105 + " did not specify uses-policy for: "
106 + mActiveAdmin.info.getTagForPolicy(reqPolicy));
107 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800108 return mActiveAdmin;
109 }
110 throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
111 }
112
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800113 void sendAdminCommandLocked(ActiveAdmin admin, String action) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800114 Intent intent = new Intent(action);
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800115 intent.setComponent(admin.info.getComponent());
Dianne Hackbornd6847842010-01-12 18:14:19 -0800116 mContext.sendBroadcast(intent);
117 }
118
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800119 void sendAdminCommandLocked(String action, int reqPolicy) {
Dianne Hackborn4141d032010-01-21 16:29:00 -0800120 if (mActiveAdmin != null) {
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800121 if (mActiveAdmin.info.usesPolicy(reqPolicy)) {
122 return;
123 }
Dianne Hackborn4141d032010-01-21 16:29:00 -0800124 sendAdminCommandLocked(mActiveAdmin, action);
125 }
126 }
127
Dianne Hackbornd6847842010-01-12 18:14:19 -0800128 ComponentName getActiveAdminLocked() {
129 if (mActiveAdmin != null) {
130 return mActiveAdmin.info.getComponent();
131 }
132 return null;
133 }
134
135 void removeActiveAdminLocked(ComponentName adminReceiver) {
136 ComponentName cur = getActiveAdminLocked();
137 if (cur != null && cur.equals(adminReceiver)) {
138 sendAdminCommandLocked(mActiveAdmin,
139 DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
140 // XXX need to wait for it to complete.
141 mActiveAdmin = null;
142 }
143 }
144
145 public DeviceAdminInfo findAdmin(ComponentName adminName) {
146 Intent resolveIntent = new Intent();
147 resolveIntent.setComponent(adminName);
148 List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
149 resolveIntent, PackageManager.GET_META_DATA);
150 if (infos == null || infos.size() <= 0) {
151 throw new IllegalArgumentException("Unknown admin: " + adminName);
152 }
153
154 try {
155 return new DeviceAdminInfo(mContext, infos.get(0));
156 } catch (XmlPullParserException e) {
157 Log.w(TAG, "Bad device admin requested: " + adminName, e);
158 return null;
159 } catch (IOException e) {
160 Log.w(TAG, "Bad device admin requested: " + adminName, e);
161 return null;
162 }
163 }
164
165 private static JournaledFile makeJournaledFile() {
166 final String base = "/data/system/device_policies.xml";
167 return new JournaledFile(new File(base), new File(base + ".tmp"));
168 }
169
170 private void saveSettingsLocked() {
171 JournaledFile journal = makeJournaledFile();
172 FileOutputStream stream = null;
173 try {
174 stream = new FileOutputStream(journal.chooseForWrite(), false);
175 XmlSerializer out = new FastXmlSerializer();
176 out.setOutput(stream, "utf-8");
177 out.startDocument(null, true);
178
179 out.startTag(null, "policies");
180
181 ActiveAdmin ap = mActiveAdmin;
182 if (ap != null) {
183 out.startTag(null, "admin");
184 out.attribute(null, "name", ap.info.getComponent().flattenToString());
185 if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
186 out.startTag(null, "password-mode");
187 out.attribute(null, "value", Integer.toString(ap.passwordMode));
188 out.endTag(null, "password-mode");
189 if (ap.minimumPasswordLength > 0) {
190 out.startTag(null, "min-password-length");
191 out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
192 out.endTag(null, "mn-password-length");
193 }
194 }
195 if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
196 out.startTag(null, "max-time-to-unlock");
197 out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
198 out.endTag(null, "max-time-to-unlock");
199 }
200 out.endTag(null, "admin");
201 }
202 out.endTag(null, "policies");
203
204 out.endDocument();
205 stream.close();
206 journal.commit();
207 } catch (IOException e) {
208 try {
209 if (stream != null) {
210 stream.close();
211 }
212 } catch (IOException ex) {
213 // Ignore
214 }
215 journal.rollback();
216 }
217 }
218
219 private void loadSettingsLocked() {
220 JournaledFile journal = makeJournaledFile();
221 FileInputStream stream = null;
222 File file = journal.chooseForRead();
223 boolean success = false;
224 try {
225 stream = new FileInputStream(file);
226 XmlPullParser parser = Xml.newPullParser();
227 parser.setInput(stream, null);
228
229 int type = parser.next();
230 while (type != XmlPullParser.START_TAG) {
231 type = parser.next();
232 }
233 String tag = parser.getName();
234 if ("policies".equals(tag)) {
235 ActiveAdmin ap = null;
236 do {
237 type = parser.next();
238 if (type == XmlPullParser.START_TAG) {
239 tag = parser.getName();
240 if (ap == null) {
241 if ("admin".equals(tag)) {
242 DeviceAdminInfo dai = findAdmin(
243 ComponentName.unflattenFromString(
244 parser.getAttributeValue(null, "name")));
245 if (dai != null) {
246 ap = new ActiveAdmin(dai);
247 }
248 }
249 } else if ("password-mode".equals(tag)) {
250 ap.passwordMode = Integer.parseInt(
251 parser.getAttributeValue(null, "value"));
252 } else if ("min-password-length".equals(tag)) {
253 ap.minimumPasswordLength = Integer.parseInt(
254 parser.getAttributeValue(null, "value"));
255 } else if ("max-time-to-unlock".equals(tag)) {
256 ap.maximumTimeToUnlock = Long.parseLong(
257 parser.getAttributeValue(null, "value"));
258 }
259 } else if (type == XmlPullParser.END_TAG) {
260 tag = parser.getName();
261 if (ap != null && "admin".equals(tag)) {
262 mActiveAdmin = ap;
263 ap = null;
264 }
265 }
266 } while (type != XmlPullParser.END_DOCUMENT);
267 success = true;
268 }
269 } catch (NullPointerException e) {
270 Log.w(TAG, "failed parsing " + file + " " + e);
271 } catch (NumberFormatException e) {
272 Log.w(TAG, "failed parsing " + file + " " + e);
273 } catch (XmlPullParserException e) {
274 Log.w(TAG, "failed parsing " + file + " " + e);
275 } catch (IOException e) {
276 Log.w(TAG, "failed parsing " + file + " " + e);
277 } catch (IndexOutOfBoundsException e) {
278 Log.w(TAG, "failed parsing " + file + " " + e);
279 }
280 try {
281 if (stream != null) {
282 stream.close();
283 }
284 } catch (IOException e) {
285 // Ignore
286 }
287
288 if (!success) {
289 Log.w(TAG, "No valid start tag found in policies file");
290 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800291
292 long timeMs = getMaximumTimeToLock();
293 if (timeMs <= 0) {
294 timeMs = Integer.MAX_VALUE;
295 }
296 try {
297 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
298 } catch (RemoteException e) {
299 Log.w(TAG, "Failure talking with power manager", e);
300 }
301
Dianne Hackbornd6847842010-01-12 18:14:19 -0800302 }
303
304 public void systemReady() {
305 synchronized (this) {
306 loadSettingsLocked();
307 }
308 }
309
310 public void setActiveAdmin(ComponentName adminReceiver) {
311 mContext.enforceCallingOrSelfPermission(
312 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
313
314 DeviceAdminInfo info = findAdmin(adminReceiver);
315 if (info == null) {
316 throw new IllegalArgumentException("Bad admin: " + adminReceiver);
317 }
318 synchronized (this) {
319 long ident = Binder.clearCallingIdentity();
320 try {
321 ComponentName cur = getActiveAdminLocked();
322 if (cur != null && cur.equals(adminReceiver)) {
323 throw new IllegalStateException("An admin is already set");
324 }
325 if (cur != null) {
326 removeActiveAdminLocked(adminReceiver);
327 }
328 mActiveAdmin = new ActiveAdmin(info);
329 saveSettingsLocked();
330 sendAdminCommandLocked(mActiveAdmin,
331 DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
332 } finally {
333 Binder.restoreCallingIdentity(ident);
334 }
335 }
336 }
337
338 public ComponentName getActiveAdmin() {
339 synchronized (this) {
340 return getActiveAdminLocked();
341 }
342 }
343
344 public void removeActiveAdmin(ComponentName adminReceiver) {
345 synchronized (this) {
346 if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
347 mContext.enforceCallingOrSelfPermission(
348 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
349 }
350 long ident = Binder.clearCallingIdentity();
351 try {
352 removeActiveAdminLocked(adminReceiver);
353 } finally {
354 Binder.restoreCallingIdentity(ident);
355 }
356 }
357 }
358
359 public void setPasswordMode(ComponentName who, int mode) {
360 synchronized (this) {
361 if (who == null) {
362 throw new NullPointerException("ComponentName is null");
363 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800364 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
365 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800366 if (ap.passwordMode != mode) {
367 ap.passwordMode = mode;
368 saveSettingsLocked();
369 }
370 }
371 }
372
373 public int getPasswordMode() {
374 synchronized (this) {
375 return mActiveAdmin != null ? mActiveAdmin.passwordMode
376 : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
377 }
378 }
379
Dianne Hackbornd6847842010-01-12 18:14:19 -0800380 public void setMinimumPasswordLength(ComponentName who, int length) {
381 synchronized (this) {
382 if (who == null) {
383 throw new NullPointerException("ComponentName is null");
384 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800385 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
386 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800387 if (ap.minimumPasswordLength != length) {
388 ap.minimumPasswordLength = length;
389 saveSettingsLocked();
390 }
391 }
392 }
393
394 public int getMinimumPasswordLength() {
395 synchronized (this) {
396 return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
397 }
398 }
399
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800400 public boolean isActivePasswordSufficient() {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800401 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.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800404 getActiveAdminForCallerLocked(null,
405 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800406 return mActivePasswordMode >= getPasswordMode()
407 && mActivePasswordLength >= getMinimumPasswordLength();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800408 }
409 }
410
411 public int getCurrentFailedPasswordAttempts() {
412 synchronized (this) {
413 // This API can only be called by an active device admin,
414 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800415 getActiveAdminForCallerLocked(null,
416 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800417 return mFailedPasswordAttempts;
418 }
419 }
420
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800421 public boolean resetPassword(String password) {
422 int mode;
423 synchronized (this) {
424 // This API can only be called by an active device admin,
425 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800426 getActiveAdminForCallerLocked(null,
427 DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800428 mode = getPasswordMode();
429 if (password.length() < getMinimumPasswordLength()) {
430 return false;
431 }
432 }
433
434 // Don't do this with the lock held, because it is going to call
435 // back in to the service.
436 long ident = Binder.clearCallingIdentity();
437 try {
438 LockPatternUtils utils = new LockPatternUtils(mContext);
439 utils.saveLockPassword(password, mode);
440 } finally {
441 Binder.restoreCallingIdentity(ident);
442 }
443
444 return true;
445 }
446
Dianne Hackbornd6847842010-01-12 18:14:19 -0800447 public void setMaximumTimeToLock(ComponentName who, long timeMs) {
448 synchronized (this) {
449 if (who == null) {
450 throw new NullPointerException("ComponentName is null");
451 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800452 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
453 DeviceAdminInfo.USES_POLICY_LIMIT_UNLOCK);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800454 if (ap.maximumTimeToUnlock != timeMs) {
455 ap.maximumTimeToUnlock = timeMs;
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800456
457 long ident = Binder.clearCallingIdentity();
458 try {
459 saveSettingsLocked();
460 if (timeMs <= 0) {
461 timeMs = Integer.MAX_VALUE;
462 }
463 try {
464 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
465 } catch (RemoteException e) {
466 Log.w(TAG, "Failure talking with power manager", e);
467 }
468 } finally {
469 Binder.restoreCallingIdentity(ident);
470 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800471 }
472 }
473 }
474
475 public long getMaximumTimeToLock() {
476 synchronized (this) {
477 return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
478 }
479 }
480
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800481 public void lockNow() {
482 synchronized (this) {
483 // This API can only be called by an active device admin,
484 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800485 getActiveAdminForCallerLocked(null,
486 DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800487 // STOPSHIP need to implement.
488 }
489 }
490
Dianne Hackbornd6847842010-01-12 18:14:19 -0800491 public void wipeData(int flags) {
492 synchronized (this) {
493 // This API can only be called by an active device admin,
494 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800495 getActiveAdminForCallerLocked(null,
496 DeviceAdminInfo.USES_POLICY_WIPE_DATA);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800497 }
498 long ident = Binder.clearCallingIdentity();
499 try {
500 RecoverySystem.rebootWipeUserData(mContext);
501 } catch (IOException e) {
502 Log.w(TAG, "Failed requesting data wipe", e);
503 } finally {
504 Binder.restoreCallingIdentity(ident);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800505 }
506 }
507
508 public void setActivePasswordState(int mode, int length) {
509 mContext.enforceCallingOrSelfPermission(
510 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
511
512 synchronized (this) {
513 if (mActivePasswordMode != mode || mActivePasswordLength != length
514 || mFailedPasswordAttempts != 0) {
515 long ident = Binder.clearCallingIdentity();
516 try {
517 mActivePasswordMode = mode;
518 mActivePasswordLength = length;
519 mFailedPasswordAttempts = 0;
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800520 sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED,
521 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800522 } finally {
523 Binder.restoreCallingIdentity(ident);
524 }
525 }
526 }
527 }
528
529 public void reportFailedPasswordAttempt() {
530 mContext.enforceCallingOrSelfPermission(
531 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
532
533 synchronized (this) {
534 long ident = Binder.clearCallingIdentity();
535 try {
536 mFailedPasswordAttempts++;
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800537 sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED,
538 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800539 } finally {
540 Binder.restoreCallingIdentity(ident);
541 }
542 }
543 }
544
545 public void reportSuccessfulPasswordAttempt() {
546 mContext.enforceCallingOrSelfPermission(
547 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
548
549 synchronized (this) {
550 if (mFailedPasswordAttempts != 0) {
551 long ident = Binder.clearCallingIdentity();
552 try {
553 mFailedPasswordAttempts = 0;
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800554 sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED,
555 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800556 } finally {
557 Binder.restoreCallingIdentity(ident);
558 }
559 }
560 }
561 }
562}