DevicePolicyManagerService.java revision 4141d035c7d41f39f023d7a051568dff87c7cd32
1/*
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;
20import com.android.internal.widget.LockPatternUtils;
21
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;
36import android.os.IBinder;
37import android.os.IPowerManager;
38import android.os.RecoverySystem;
39import android.os.RemoteException;
40import android.os.ServiceManager;
41import 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
58    IPowerManager mIPowerManager;
59
60    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
86    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
94    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
95        if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) {
96            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
114    void sendAdminCommandLocked(String action) {
115        if (mActiveAdmin != null) {
116            sendAdminCommandLocked(mActiveAdmin, action);
117        }
118    }
119
120    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        }
283
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
294    }
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
371    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
390    public boolean isActivePasswordSufficient() {
391        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);
395            return mActivePasswordMode >= getPasswordMode()
396                    && mActivePasswordLength >= getMinimumPasswordLength();
397        }
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
409    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
434    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;
442
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                }
457            }
458        }
459    }
460
461    public long getMaximumTimeToLock() {
462        synchronized (this) {
463            return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
464        }
465    }
466
467    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
476    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);
481        }
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);
489        }
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;
504                    sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED);
505                } 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++;
520                sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED);
521            } 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;
536                    sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
537                } finally {
538                    Binder.restoreCallingIdentity(ident);
539                }
540            }
541        }
542    }
543}
544