blob: fd425381d413a1c3d5087193ad46bd2ae3981646 [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;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080031import android.content.ContentResolver;
Dianne Hackbornd6847842010-01-12 18:14:19 -080032import android.content.Context;
33import android.content.Intent;
34import android.content.pm.PackageManager;
35import android.content.pm.ResolveInfo;
36import android.os.Binder;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080037import android.os.IBinder;
38import android.os.IPowerManager;
39import android.os.PowerManager;
40import android.os.RecoverySystem;
41import android.os.RemoteException;
42import android.os.ServiceManager;
43import android.provider.Settings;
Dianne Hackbornd6847842010-01-12 18:14:19 -080044import android.util.Log;
45import android.util.Xml;
46
47import java.io.File;
48import java.io.FileInputStream;
49import java.io.FileOutputStream;
50import java.io.IOException;
51import java.util.List;
52
53/**
54 * Implementation of the device policy APIs.
55 */
56public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
57 private static final String TAG = "DevicePolicyManagerService";
58
59 private final Context mContext;
60
Dianne Hackborndf83afa2010-01-20 13:37:26 -080061 IPowerManager mIPowerManager;
62
Dianne Hackbornd6847842010-01-12 18:14:19 -080063 int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
64 int mActivePasswordLength = 0;
65 int mFailedPasswordAttempts = 0;
66
67 ActiveAdmin mActiveAdmin;
68
69 static class ActiveAdmin {
70 ActiveAdmin(DeviceAdminInfo _info) {
71 info = _info;
72 }
73
74 final DeviceAdminInfo info;
75 int getUid() { return info.getActivityInfo().applicationInfo.uid; }
76
77 int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
78 int minimumPasswordLength = 0;
79 long maximumTimeToUnlock = 0;
80 }
81
82 /**
83 * Instantiates the service.
84 */
85 public DevicePolicyManagerService(Context context) {
86 mContext = context;
87 }
88
Dianne Hackborndf83afa2010-01-20 13:37:26 -080089 private IPowerManager getIPowerManager() {
90 if (mIPowerManager == null) {
91 IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
92 mIPowerManager = IPowerManager.Stub.asInterface(b);
93 }
94 return mIPowerManager;
95 }
96
Dianne Hackbornd6847842010-01-12 18:14:19 -080097 ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
Dianne Hackborndf83afa2010-01-20 13:37:26 -080098 if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) {
Dianne Hackbornd6847842010-01-12 18:14:19 -080099 if (who != null) {
100 if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
101 || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
102 throw new SecurityException("Current admin is not " + who);
103 }
104 }
105 return mActiveAdmin;
106 }
107 throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
108 }
109
110
111 void sendAdminCommandLocked(ActiveAdmin policy, String action) {
112 Intent intent = new Intent(action);
113 intent.setComponent(policy.info.getComponent());
114 mContext.sendBroadcast(intent);
115 }
116
117 ComponentName getActiveAdminLocked() {
118 if (mActiveAdmin != null) {
119 return mActiveAdmin.info.getComponent();
120 }
121 return null;
122 }
123
124 void removeActiveAdminLocked(ComponentName adminReceiver) {
125 ComponentName cur = getActiveAdminLocked();
126 if (cur != null && cur.equals(adminReceiver)) {
127 sendAdminCommandLocked(mActiveAdmin,
128 DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
129 // XXX need to wait for it to complete.
130 mActiveAdmin = null;
131 }
132 }
133
134 public DeviceAdminInfo findAdmin(ComponentName adminName) {
135 Intent resolveIntent = new Intent();
136 resolveIntent.setComponent(adminName);
137 List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
138 resolveIntent, PackageManager.GET_META_DATA);
139 if (infos == null || infos.size() <= 0) {
140 throw new IllegalArgumentException("Unknown admin: " + adminName);
141 }
142
143 try {
144 return new DeviceAdminInfo(mContext, infos.get(0));
145 } catch (XmlPullParserException e) {
146 Log.w(TAG, "Bad device admin requested: " + adminName, e);
147 return null;
148 } catch (IOException e) {
149 Log.w(TAG, "Bad device admin requested: " + adminName, e);
150 return null;
151 }
152 }
153
154 private static JournaledFile makeJournaledFile() {
155 final String base = "/data/system/device_policies.xml";
156 return new JournaledFile(new File(base), new File(base + ".tmp"));
157 }
158
159 private void saveSettingsLocked() {
160 JournaledFile journal = makeJournaledFile();
161 FileOutputStream stream = null;
162 try {
163 stream = new FileOutputStream(journal.chooseForWrite(), false);
164 XmlSerializer out = new FastXmlSerializer();
165 out.setOutput(stream, "utf-8");
166 out.startDocument(null, true);
167
168 out.startTag(null, "policies");
169
170 ActiveAdmin ap = mActiveAdmin;
171 if (ap != null) {
172 out.startTag(null, "admin");
173 out.attribute(null, "name", ap.info.getComponent().flattenToString());
174 if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
175 out.startTag(null, "password-mode");
176 out.attribute(null, "value", Integer.toString(ap.passwordMode));
177 out.endTag(null, "password-mode");
178 if (ap.minimumPasswordLength > 0) {
179 out.startTag(null, "min-password-length");
180 out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
181 out.endTag(null, "mn-password-length");
182 }
183 }
184 if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
185 out.startTag(null, "max-time-to-unlock");
186 out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
187 out.endTag(null, "max-time-to-unlock");
188 }
189 out.endTag(null, "admin");
190 }
191 out.endTag(null, "policies");
192
193 out.endDocument();
194 stream.close();
195 journal.commit();
196 } catch (IOException e) {
197 try {
198 if (stream != null) {
199 stream.close();
200 }
201 } catch (IOException ex) {
202 // Ignore
203 }
204 journal.rollback();
205 }
206 }
207
208 private void loadSettingsLocked() {
209 JournaledFile journal = makeJournaledFile();
210 FileInputStream stream = null;
211 File file = journal.chooseForRead();
212 boolean success = false;
213 try {
214 stream = new FileInputStream(file);
215 XmlPullParser parser = Xml.newPullParser();
216 parser.setInput(stream, null);
217
218 int type = parser.next();
219 while (type != XmlPullParser.START_TAG) {
220 type = parser.next();
221 }
222 String tag = parser.getName();
223 if ("policies".equals(tag)) {
224 ActiveAdmin ap = null;
225 do {
226 type = parser.next();
227 if (type == XmlPullParser.START_TAG) {
228 tag = parser.getName();
229 if (ap == null) {
230 if ("admin".equals(tag)) {
231 DeviceAdminInfo dai = findAdmin(
232 ComponentName.unflattenFromString(
233 parser.getAttributeValue(null, "name")));
234 if (dai != null) {
235 ap = new ActiveAdmin(dai);
236 }
237 }
238 } else if ("password-mode".equals(tag)) {
239 ap.passwordMode = Integer.parseInt(
240 parser.getAttributeValue(null, "value"));
241 } else if ("min-password-length".equals(tag)) {
242 ap.minimumPasswordLength = Integer.parseInt(
243 parser.getAttributeValue(null, "value"));
244 } else if ("max-time-to-unlock".equals(tag)) {
245 ap.maximumTimeToUnlock = Long.parseLong(
246 parser.getAttributeValue(null, "value"));
247 }
248 } else if (type == XmlPullParser.END_TAG) {
249 tag = parser.getName();
250 if (ap != null && "admin".equals(tag)) {
251 mActiveAdmin = ap;
252 ap = null;
253 }
254 }
255 } while (type != XmlPullParser.END_DOCUMENT);
256 success = true;
257 }
258 } catch (NullPointerException e) {
259 Log.w(TAG, "failed parsing " + file + " " + e);
260 } catch (NumberFormatException e) {
261 Log.w(TAG, "failed parsing " + file + " " + e);
262 } catch (XmlPullParserException e) {
263 Log.w(TAG, "failed parsing " + file + " " + e);
264 } catch (IOException e) {
265 Log.w(TAG, "failed parsing " + file + " " + e);
266 } catch (IndexOutOfBoundsException e) {
267 Log.w(TAG, "failed parsing " + file + " " + e);
268 }
269 try {
270 if (stream != null) {
271 stream.close();
272 }
273 } catch (IOException e) {
274 // Ignore
275 }
276
277 if (!success) {
278 Log.w(TAG, "No valid start tag found in policies file");
279 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800280
281 long timeMs = getMaximumTimeToLock();
282 if (timeMs <= 0) {
283 timeMs = Integer.MAX_VALUE;
284 }
285 try {
286 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
287 } catch (RemoteException e) {
288 Log.w(TAG, "Failure talking with power manager", e);
289 }
290
Dianne Hackbornd6847842010-01-12 18:14:19 -0800291 }
292
293 public void systemReady() {
294 synchronized (this) {
295 loadSettingsLocked();
296 }
297 }
298
299 public void setActiveAdmin(ComponentName adminReceiver) {
300 mContext.enforceCallingOrSelfPermission(
301 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
302
303 DeviceAdminInfo info = findAdmin(adminReceiver);
304 if (info == null) {
305 throw new IllegalArgumentException("Bad admin: " + adminReceiver);
306 }
307 synchronized (this) {
308 long ident = Binder.clearCallingIdentity();
309 try {
310 ComponentName cur = getActiveAdminLocked();
311 if (cur != null && cur.equals(adminReceiver)) {
312 throw new IllegalStateException("An admin is already set");
313 }
314 if (cur != null) {
315 removeActiveAdminLocked(adminReceiver);
316 }
317 mActiveAdmin = new ActiveAdmin(info);
318 saveSettingsLocked();
319 sendAdminCommandLocked(mActiveAdmin,
320 DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
321 } finally {
322 Binder.restoreCallingIdentity(ident);
323 }
324 }
325 }
326
327 public ComponentName getActiveAdmin() {
328 synchronized (this) {
329 return getActiveAdminLocked();
330 }
331 }
332
333 public void removeActiveAdmin(ComponentName adminReceiver) {
334 synchronized (this) {
335 if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
336 mContext.enforceCallingOrSelfPermission(
337 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
338 }
339 long ident = Binder.clearCallingIdentity();
340 try {
341 removeActiveAdminLocked(adminReceiver);
342 } finally {
343 Binder.restoreCallingIdentity(ident);
344 }
345 }
346 }
347
348 public void setPasswordMode(ComponentName who, int mode) {
349 synchronized (this) {
350 if (who == null) {
351 throw new NullPointerException("ComponentName is null");
352 }
353 ActiveAdmin ap = getActiveAdminForCallerLocked(who);
354 if (ap.passwordMode != mode) {
355 ap.passwordMode = mode;
356 saveSettingsLocked();
357 }
358 }
359 }
360
361 public int getPasswordMode() {
362 synchronized (this) {
363 return mActiveAdmin != null ? mActiveAdmin.passwordMode
364 : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
365 }
366 }
367
Dianne Hackbornd6847842010-01-12 18:14:19 -0800368 public void setMinimumPasswordLength(ComponentName who, int length) {
369 synchronized (this) {
370 if (who == null) {
371 throw new NullPointerException("ComponentName is null");
372 }
373 ActiveAdmin ap = getActiveAdminForCallerLocked(who);
374 if (ap.minimumPasswordLength != length) {
375 ap.minimumPasswordLength = length;
376 saveSettingsLocked();
377 }
378 }
379 }
380
381 public int getMinimumPasswordLength() {
382 synchronized (this) {
383 return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
384 }
385 }
386
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800387 public boolean isActivePasswordSufficient() {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800388 synchronized (this) {
389 // This API can only be called by an active device admin,
390 // so try to retrieve it to check that the caller is one.
391 getActiveAdminForCallerLocked(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800392 return mActivePasswordMode >= getPasswordMode()
393 && mActivePasswordLength >= getMinimumPasswordLength();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800394 }
395 }
396
397 public int getCurrentFailedPasswordAttempts() {
398 synchronized (this) {
399 // This API can only be called by an active device admin,
400 // so try to retrieve it to check that the caller is one.
401 getActiveAdminForCallerLocked(null);
402 return mFailedPasswordAttempts;
403 }
404 }
405
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800406 public boolean resetPassword(String password) {
407 int mode;
408 synchronized (this) {
409 // This API can only be called by an active device admin,
410 // so try to retrieve it to check that the caller is one.
411 getActiveAdminForCallerLocked(null);
412 mode = getPasswordMode();
413 if (password.length() < getMinimumPasswordLength()) {
414 return false;
415 }
416 }
417
418 // Don't do this with the lock held, because it is going to call
419 // back in to the service.
420 long ident = Binder.clearCallingIdentity();
421 try {
422 LockPatternUtils utils = new LockPatternUtils(mContext);
423 utils.saveLockPassword(password, mode);
424 } finally {
425 Binder.restoreCallingIdentity(ident);
426 }
427
428 return true;
429 }
430
Dianne Hackbornd6847842010-01-12 18:14:19 -0800431 public void setMaximumTimeToLock(ComponentName who, long timeMs) {
432 synchronized (this) {
433 if (who == null) {
434 throw new NullPointerException("ComponentName is null");
435 }
436 ActiveAdmin ap = getActiveAdminForCallerLocked(who);
437 if (ap.maximumTimeToUnlock != timeMs) {
438 ap.maximumTimeToUnlock = timeMs;
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800439
440 long ident = Binder.clearCallingIdentity();
441 try {
442 saveSettingsLocked();
443 if (timeMs <= 0) {
444 timeMs = Integer.MAX_VALUE;
445 }
446 try {
447 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
448 } catch (RemoteException e) {
449 Log.w(TAG, "Failure talking with power manager", e);
450 }
451 } finally {
452 Binder.restoreCallingIdentity(ident);
453 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800454 }
455 }
456 }
457
458 public long getMaximumTimeToLock() {
459 synchronized (this) {
460 return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
461 }
462 }
463
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800464 public void lockNow() {
465 synchronized (this) {
466 // This API can only be called by an active device admin,
467 // so try to retrieve it to check that the caller is one.
468 getActiveAdminForCallerLocked(null);
469 // STOPSHIP need to implement.
470 }
471 }
472
Dianne Hackbornd6847842010-01-12 18:14:19 -0800473 public void wipeData(int flags) {
474 synchronized (this) {
475 // This API can only be called by an active device admin,
476 // so try to retrieve it to check that the caller is one.
477 getActiveAdminForCallerLocked(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800478 }
479 long ident = Binder.clearCallingIdentity();
480 try {
481 RecoverySystem.rebootWipeUserData(mContext);
482 } catch (IOException e) {
483 Log.w(TAG, "Failed requesting data wipe", e);
484 } finally {
485 Binder.restoreCallingIdentity(ident);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800486 }
487 }
488
489 public void setActivePasswordState(int mode, int length) {
490 mContext.enforceCallingOrSelfPermission(
491 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
492
493 synchronized (this) {
494 if (mActivePasswordMode != mode || mActivePasswordLength != length
495 || mFailedPasswordAttempts != 0) {
496 long ident = Binder.clearCallingIdentity();
497 try {
498 mActivePasswordMode = mode;
499 mActivePasswordLength = length;
500 mFailedPasswordAttempts = 0;
501 sendAdminCommandLocked(mActiveAdmin,
502 DeviceAdmin.ACTION_PASSWORD_CHANGED);
503 } finally {
504 Binder.restoreCallingIdentity(ident);
505 }
506 }
507 }
508 }
509
510 public void reportFailedPasswordAttempt() {
511 mContext.enforceCallingOrSelfPermission(
512 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
513
514 synchronized (this) {
515 long ident = Binder.clearCallingIdentity();
516 try {
517 mFailedPasswordAttempts++;
518 sendAdminCommandLocked(mActiveAdmin,
519 DeviceAdmin.ACTION_PASSWORD_FAILED);
520 } finally {
521 Binder.restoreCallingIdentity(ident);
522 }
523 }
524 }
525
526 public void reportSuccessfulPasswordAttempt() {
527 mContext.enforceCallingOrSelfPermission(
528 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
529
530 synchronized (this) {
531 if (mFailedPasswordAttempts != 0) {
532 long ident = Binder.clearCallingIdentity();
533 try {
534 mFailedPasswordAttempts = 0;
535 sendAdminCommandLocked(mActiveAdmin,
536 DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
537 } finally {
538 Binder.restoreCallingIdentity(ident);
539 }
540 }
541 }
542 }
543}