blob: b2d534b643707b300382973d2b5a2b04226bb905 [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
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080019import com.android.internal.content.PackageMonitor;
Dianne Hackborn42499172010-10-15 18:45:07 -070020import com.android.internal.os.storage.ExternalStorageFormatter;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080021import com.android.internal.util.FastXmlSerializer;
Dianne Hackborn1afd1c92010-03-18 22:47:17 -070022import com.android.internal.util.JournaledFile;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080023import com.android.internal.util.XmlUtils;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080024import com.android.internal.widget.LockPatternUtils;
Dianne Hackbornd6847842010-01-12 18:14:19 -080025
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlPullParserException;
28import org.xmlpull.v1.XmlSerializer;
29
Dianne Hackborn8ea138c2010-01-26 18:01:04 -080030import android.app.Activity;
Jim Millera4e28d12010-11-08 16:15:47 -080031import android.app.AlarmManager;
32import android.app.PendingIntent;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080033import android.app.admin.DeviceAdminInfo;
34import android.app.admin.DeviceAdminReceiver;
35import android.app.admin.DevicePolicyManager;
36import android.app.admin.IDevicePolicyManager;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -080037import android.content.BroadcastReceiver;
Dianne Hackbornd6847842010-01-12 18:14:19 -080038import android.content.ComponentName;
Oscar Montemayor69238c62010-08-03 10:51:06 -070039import android.content.ContentResolver;
Dianne Hackbornd6847842010-01-12 18:14:19 -080040import android.content.Context;
41import android.content.Intent;
Jim Millera4e28d12010-11-08 16:15:47 -080042import android.content.IntentFilter;
Dianne Hackbornd6847842010-01-12 18:14:19 -080043import android.content.pm.PackageManager;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080044import android.content.pm.PackageManager.NameNotFoundException;
Andy Stadler1f35d482010-11-19 15:39:41 -080045import android.content.pm.ResolveInfo;
Dianne Hackbornd6847842010-01-12 18:14:19 -080046import android.os.Binder;
Jim Millera4e28d12010-11-08 16:15:47 -080047import android.os.Handler;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080048import android.os.IBinder;
49import android.os.IPowerManager;
Dianne Hackborn42499172010-10-15 18:45:07 -070050import android.os.PowerManager;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080051import android.os.RecoverySystem;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -080052import android.os.RemoteCallback;
Dianne Hackborndf83afa2010-01-20 13:37:26 -080053import android.os.RemoteException;
54import android.os.ServiceManager;
Dianne Hackborn254cb442010-01-27 19:23:59 -080055import android.os.SystemClock;
Andy Stadler0fe45de2011-01-20 16:35:09 -080056import android.os.SystemProperties;
Oscar Montemayor69238c62010-08-03 10:51:06 -070057import android.provider.Settings;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080058import android.util.PrintWriterPrinter;
59import android.util.Printer;
Andy Stadler1f35d482010-11-19 15:39:41 -080060import android.util.Slog;
Dianne Hackbornd6847842010-01-12 18:14:19 -080061import android.util.Xml;
Dianne Hackborn254cb442010-01-27 19:23:59 -080062import android.view.WindowManagerPolicy;
Dianne Hackbornd6847842010-01-12 18:14:19 -080063
64import java.io.File;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080065import java.io.FileDescriptor;
Dianne Hackbornd6847842010-01-12 18:14:19 -080066import java.io.FileInputStream;
Dianne Hackborncef65ee2010-09-30 18:27:22 -070067import java.io.FileNotFoundException;
Dianne Hackbornd6847842010-01-12 18:14:19 -080068import java.io.FileOutputStream;
69import java.io.IOException;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080070import java.io.PrintWriter;
Jim Millera4e28d12010-11-08 16:15:47 -080071import java.text.DateFormat;
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -080072import java.util.ArrayList;
Jim Millera4e28d12010-11-08 16:15:47 -080073import java.util.Date;
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -080074import java.util.HashMap;
Dianne Hackbornd6847842010-01-12 18:14:19 -080075import java.util.List;
Oscar Montemayor69238c62010-08-03 10:51:06 -070076import java.util.Set;
Dianne Hackbornd6847842010-01-12 18:14:19 -080077
78/**
79 * Implementation of the device policy APIs.
80 */
81public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Jim Millera4e28d12010-11-08 16:15:47 -080082 private static final int REQUEST_EXPIRE_PASSWORD = 5571;
83
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080084 static final String TAG = "DevicePolicyManagerService";
Konstantin Lopyrev32558232010-05-20 16:18:05 -070085
Jim Millera4e28d12010-11-08 16:15:47 -080086 private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * 86400 * 1000; // 5 days, in ms
87
88 protected static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
89 = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
90
91 private static final long MS_PER_DAY = 86400 * 1000;
Jim Millera4e28d12010-11-08 16:15:47 -080092
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080093 final Context mContext;
94 final MyPackageMonitor mMonitor;
Dianne Hackborn42499172010-10-15 18:45:07 -070095 final PowerManager.WakeLock mWakeLock;
Dianne Hackbornd6847842010-01-12 18:14:19 -080096
Dianne Hackborndf83afa2010-01-20 13:37:26 -080097 IPowerManager mIPowerManager;
Konstantin Lopyrev32558232010-05-20 16:18:05 -070098
Dianne Hackborn9327f4f2010-01-29 10:38:29 -080099 int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800100 int mActivePasswordLength = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700101 int mActivePasswordUpperCase = 0;
102 int mActivePasswordLowerCase = 0;
103 int mActivePasswordLetters = 0;
104 int mActivePasswordNumeric = 0;
105 int mActivePasswordSymbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700106 int mActivePasswordNonLetter = 0;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800107 int mFailedPasswordAttempts = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700108
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800109 int mPasswordOwner = -1;
Jim Millera4e28d12010-11-08 16:15:47 -0800110 Handler mHandler = new Handler();
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700111
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800112 final HashMap<ComponentName, ActiveAdmin> mAdminMap
113 = new HashMap<ComponentName, ActiveAdmin>();
114 final ArrayList<ActiveAdmin> mAdminList
115 = new ArrayList<ActiveAdmin>();
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700116
Jim Millera4e28d12010-11-08 16:15:47 -0800117 BroadcastReceiver mReceiver = new BroadcastReceiver() {
118 @Override
119 public void onReceive(Context context, Intent intent) {
120 String action = intent.getAction();
121 if (Intent.ACTION_BOOT_COMPLETED.equals(action)
122 || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
123 Slog.v(TAG, "Sending password expiration notifications for action " + action);
124 mHandler.post(new Runnable() {
125 public void run() {
126 handlePasswordExpirationNotification();
127 }
128 });
129 }
130 }
131 };
132
Dianne Hackbornd6847842010-01-12 18:14:19 -0800133 static class ActiveAdmin {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800134 final DeviceAdminInfo info;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700135
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800136 int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800137 int minimumPasswordLength = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700138 int passwordHistoryLength = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700139 int minimumPasswordUpperCase = 0;
140 int minimumPasswordLowerCase = 0;
141 int minimumPasswordLetters = 1;
142 int minimumPasswordNumeric = 1;
143 int minimumPasswordSymbols = 1;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700144 int minimumPasswordNonLetter = 0;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800145 long maximumTimeToUnlock = 0;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800146 int maximumFailedPasswordsForWipe = 0;
Jim Millera4e28d12010-11-08 16:15:47 -0800147 long passwordExpirationTimeout = 0L;
148 long passwordExpirationDate = 0L;
Andy Stadler22dbfda2011-01-17 12:47:31 -0800149 boolean encryptionRequested = false;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700150
Oscar Montemayor69238c62010-08-03 10:51:06 -0700151 // TODO: review implementation decisions with frameworks team
152 boolean specifiesGlobalProxy = false;
153 String globalProxySpec = null;
154 String globalProxyExclusionList = null;
155
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800156 ActiveAdmin(DeviceAdminInfo _info) {
157 info = _info;
158 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700159
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800160 int getUid() { return info.getActivityInfo().applicationInfo.uid; }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700161
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800162 void writeToXml(XmlSerializer out)
163 throws IllegalArgumentException, IllegalStateException, IOException {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800164 out.startTag(null, "policies");
165 info.writePoliciesToXml(out);
166 out.endTag(null, "policies");
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800167 if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
168 out.startTag(null, "password-quality");
169 out.attribute(null, "value", Integer.toString(passwordQuality));
170 out.endTag(null, "password-quality");
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800171 if (minimumPasswordLength > 0) {
172 out.startTag(null, "min-password-length");
173 out.attribute(null, "value", Integer.toString(minimumPasswordLength));
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700174 out.endTag(null, "min-password-length");
175 }
176 if(passwordHistoryLength > 0) {
177 out.startTag(null, "password-history-length");
178 out.attribute(null, "value", Integer.toString(passwordHistoryLength));
179 out.endTag(null, "password-history-length");
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800180 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700181 if (minimumPasswordUpperCase > 0) {
182 out.startTag(null, "min-password-uppercase");
183 out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase));
184 out.endTag(null, "min-password-uppercase");
185 }
186 if (minimumPasswordLowerCase > 0) {
187 out.startTag(null, "min-password-lowercase");
188 out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase));
189 out.endTag(null, "min-password-lowercase");
190 }
191 if (minimumPasswordLetters > 0) {
192 out.startTag(null, "min-password-letters");
193 out.attribute(null, "value", Integer.toString(minimumPasswordLetters));
194 out.endTag(null, "min-password-letters");
195 }
196 if (minimumPasswordNumeric > 0) {
197 out.startTag(null, "min-password-numeric");
198 out.attribute(null, "value", Integer.toString(minimumPasswordNumeric));
199 out.endTag(null, "min-password-numeric");
200 }
201 if (minimumPasswordSymbols > 0) {
202 out.startTag(null, "min-password-symbols");
203 out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
204 out.endTag(null, "min-password-symbols");
205 }
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700206 if (minimumPasswordNonLetter > 0) {
207 out.startTag(null, "min-password-nonletter");
208 out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
209 out.endTag(null, "min-password-nonletter");
210 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800211 }
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700212 if (maximumTimeToUnlock != 0) {
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800213 out.startTag(null, "max-time-to-unlock");
214 out.attribute(null, "value", Long.toString(maximumTimeToUnlock));
215 out.endTag(null, "max-time-to-unlock");
216 }
217 if (maximumFailedPasswordsForWipe != 0) {
218 out.startTag(null, "max-failed-password-wipe");
219 out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe));
220 out.endTag(null, "max-failed-password-wipe");
221 }
Oscar Montemayor69238c62010-08-03 10:51:06 -0700222 if (specifiesGlobalProxy) {
223 out.startTag(null, "specifies-global-proxy");
224 out.attribute(null, "value", Boolean.toString(specifiesGlobalProxy));
225 out.endTag(null, "specifies_global_proxy");
226 if (globalProxySpec != null) {
227 out.startTag(null, "global-proxy-spec");
228 out.attribute(null, "value", globalProxySpec);
229 out.endTag(null, "global-proxy-spec");
230 }
231 if (globalProxyExclusionList != null) {
232 out.startTag(null, "global-proxy-exclusion-list");
233 out.attribute(null, "value", globalProxyExclusionList);
234 out.endTag(null, "global-proxy-exclusion-list");
235 }
236 }
Jim Millera4e28d12010-11-08 16:15:47 -0800237 if (passwordExpirationTimeout != 0L) {
238 out.startTag(null, "password-expiration-timeout");
239 out.attribute(null, "value", Long.toString(passwordExpirationTimeout));
240 out.endTag(null, "password-expiration-timeout");
241 }
242 if (passwordExpirationDate != 0L) {
243 out.startTag(null, "password-expiration-date");
244 out.attribute(null, "value", Long.toString(passwordExpirationDate));
245 out.endTag(null, "password-expiration-date");
246 }
Andy Stadler22dbfda2011-01-17 12:47:31 -0800247 if (encryptionRequested) {
248 out.startTag(null, "encryption-requested");
249 out.attribute(null, "value", Boolean.toString(encryptionRequested));
250 out.endTag(null, "encryption-requested");
251 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800252 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700253
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800254 void readFromXml(XmlPullParser parser)
255 throws XmlPullParserException, IOException {
256 int outerDepth = parser.getDepth();
257 int type;
258 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
259 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
260 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
261 continue;
262 }
263 String tag = parser.getName();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800264 if ("policies".equals(tag)) {
265 info.readPoliciesFromXml(parser);
266 } else if ("password-quality".equals(tag)) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800267 passwordQuality = Integer.parseInt(
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800268 parser.getAttributeValue(null, "value"));
269 } else if ("min-password-length".equals(tag)) {
270 minimumPasswordLength = Integer.parseInt(
271 parser.getAttributeValue(null, "value"));
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700272 } else if ("password-history-length".equals(tag)) {
273 passwordHistoryLength = Integer.parseInt(
274 parser.getAttributeValue(null, "value"));
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700275 } else if ("min-password-uppercase".equals(tag)) {
276 minimumPasswordUpperCase = Integer.parseInt(
277 parser.getAttributeValue(null, "value"));
278 } else if ("min-password-lowercase".equals(tag)) {
279 minimumPasswordLowerCase = Integer.parseInt(
280 parser.getAttributeValue(null, "value"));
281 } else if ("min-password-letters".equals(tag)) {
282 minimumPasswordLetters = Integer.parseInt(
283 parser.getAttributeValue(null, "value"));
284 } else if ("min-password-numeric".equals(tag)) {
285 minimumPasswordNumeric = Integer.parseInt(
286 parser.getAttributeValue(null, "value"));
287 } else if ("min-password-symbols".equals(tag)) {
288 minimumPasswordSymbols = Integer.parseInt(
289 parser.getAttributeValue(null, "value"));
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700290 } else if ("min-password-nonletter".equals(tag)) {
291 minimumPasswordNonLetter = Integer.parseInt(
292 parser.getAttributeValue(null, "value"));
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800293 } else if ("max-time-to-unlock".equals(tag)) {
294 maximumTimeToUnlock = Long.parseLong(
295 parser.getAttributeValue(null, "value"));
296 } else if ("max-failed-password-wipe".equals(tag)) {
297 maximumFailedPasswordsForWipe = Integer.parseInt(
298 parser.getAttributeValue(null, "value"));
Oscar Montemayor69238c62010-08-03 10:51:06 -0700299 } else if ("specifies-global-proxy".equals(tag)) {
Andy Stadler22dbfda2011-01-17 12:47:31 -0800300 specifiesGlobalProxy = Boolean.parseBoolean(
Oscar Montemayor69238c62010-08-03 10:51:06 -0700301 parser.getAttributeValue(null, "value"));
302 } else if ("global-proxy-spec".equals(tag)) {
303 globalProxySpec =
304 parser.getAttributeValue(null, "value");
305 } else if ("global-proxy-exclusion-list".equals(tag)) {
306 globalProxyExclusionList =
307 parser.getAttributeValue(null, "value");
Jim Millera4e28d12010-11-08 16:15:47 -0800308 } else if ("password-expiration-timeout".equals(tag)) {
309 passwordExpirationTimeout = Long.parseLong(
310 parser.getAttributeValue(null, "value"));
311 } else if ("password-expiration-date".equals(tag)) {
312 passwordExpirationDate = Long.parseLong(
313 parser.getAttributeValue(null, "value"));
Andy Stadler22dbfda2011-01-17 12:47:31 -0800314 } else if ("encryption-requested".equals(tag)) {
315 encryptionRequested = Boolean.parseBoolean(
316 parser.getAttributeValue(null, "value"));
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800317 } else {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700318 Slog.w(TAG, "Unknown admin tag: " + tag);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800319 }
320 XmlUtils.skipCurrentTag(parser);
321 }
322 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700323
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800324 void dump(String prefix, PrintWriter pw) {
325 pw.print(prefix); pw.print("uid="); pw.println(getUid());
326 pw.print(prefix); pw.println("policies:");
327 ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
328 if (pols != null) {
329 for (int i=0; i<pols.size(); i++) {
330 pw.print(prefix); pw.print(" "); pw.println(pols.get(i).tag);
331 }
332 }
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700333 pw.print(prefix); pw.print("passwordQuality=0x");
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700334 pw.println(Integer.toHexString(passwordQuality));
335 pw.print(prefix); pw.print("minimumPasswordLength=");
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800336 pw.println(minimumPasswordLength);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700337 pw.print(prefix); pw.print("passwordHistoryLength=");
338 pw.println(passwordHistoryLength);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700339 pw.print(prefix); pw.print("minimumPasswordUpperCase=");
340 pw.println(minimumPasswordUpperCase);
341 pw.print(prefix); pw.print("minimumPasswordLowerCase=");
342 pw.println(minimumPasswordLowerCase);
343 pw.print(prefix); pw.print("minimumPasswordLetters=");
344 pw.println(minimumPasswordLetters);
345 pw.print(prefix); pw.print("minimumPasswordNumeric=");
346 pw.println(minimumPasswordNumeric);
347 pw.print(prefix); pw.print("minimumPasswordSymbols=");
348 pw.println(minimumPasswordSymbols);
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700349 pw.print(prefix); pw.print("minimumPasswordNonLetter=");
350 pw.println(minimumPasswordNonLetter);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800351 pw.print(prefix); pw.print("maximumTimeToUnlock=");
352 pw.println(maximumTimeToUnlock);
353 pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
354 pw.println(maximumFailedPasswordsForWipe);
Oscar Montemayor69238c62010-08-03 10:51:06 -0700355 pw.print(prefix); pw.print("specifiesGlobalProxy=");
356 pw.println(specifiesGlobalProxy);
Jim Millera4e28d12010-11-08 16:15:47 -0800357 pw.print(prefix); pw.print("passwordExpirationTimeout=");
358 pw.println(passwordExpirationTimeout);
359 pw.print(prefix); pw.print("passwordExpirationDate=");
360 pw.println(passwordExpirationDate);
Oscar Montemayor69238c62010-08-03 10:51:06 -0700361 if (globalProxySpec != null) {
362 pw.print(prefix); pw.print("globalProxySpec=");
363 pw.println(globalProxySpec);
364 }
365 if (globalProxyExclusionList != null) {
366 pw.print(prefix); pw.print("globalProxyEclusionList=");
367 pw.println(globalProxyExclusionList);
368 }
Andy Stadler22dbfda2011-01-17 12:47:31 -0800369 pw.print(prefix); pw.print("encryptionRequested=");
370 pw.println(encryptionRequested);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800371 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800372 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700373
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800374 class MyPackageMonitor extends PackageMonitor {
Andy Stadler1f35d482010-11-19 15:39:41 -0800375 @Override
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800376 public void onSomePackagesChanged() {
377 synchronized (DevicePolicyManagerService.this) {
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800378 boolean removed = false;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800379 for (int i=mAdminList.size()-1; i>=0; i--) {
380 ActiveAdmin aa = mAdminList.get(i);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700381 int change = isPackageDisappearing(aa.info.getPackageName());
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800382 if (change == PACKAGE_PERMANENT_CHANGE
383 || change == PACKAGE_TEMPORARY_CHANGE) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700384 Slog.w(TAG, "Admin unexpectedly uninstalled: "
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800385 + aa.info.getComponent());
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800386 removed = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800387 mAdminList.remove(i);
388 } else if (isPackageModified(aa.info.getPackageName())) {
389 try {
390 mContext.getPackageManager().getReceiverInfo(
391 aa.info.getComponent(), 0);
392 } catch (NameNotFoundException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700393 Slog.w(TAG, "Admin package change removed component: "
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800394 + aa.info.getComponent());
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800395 removed = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800396 mAdminList.remove(i);
397 }
398 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800399 }
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800400 if (removed) {
401 validatePasswordOwnerLocked();
402 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800403 }
404 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800405 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700406
Dianne Hackbornd6847842010-01-12 18:14:19 -0800407 /**
408 * Instantiates the service.
409 */
410 public DevicePolicyManagerService(Context context) {
411 mContext = context;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800412 mMonitor = new MyPackageMonitor();
413 mMonitor.register(context, true);
Dianne Hackborn42499172010-10-15 18:45:07 -0700414 mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
415 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
Jim Millera4e28d12010-11-08 16:15:47 -0800416 IntentFilter filter = new IntentFilter();
417 filter.addAction(Intent.ACTION_BOOT_COMPLETED);
418 filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
419 context.registerReceiver(mReceiver, filter);
420 }
421
Andy Stadler043116a2010-11-29 17:43:32 -0800422 /**
423 * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
424 * reminders. Clears alarm if no expirations are configured.
425 */
Jim Millera4e28d12010-11-08 16:15:47 -0800426 protected void setExpirationAlarmCheckLocked(Context context) {
427 final long expiration = getPasswordExpirationLocked(null);
428 final long now = System.currentTimeMillis();
429 final long timeToExpire = expiration - now;
430 final long alarmTime;
Andy Stadler043116a2010-11-29 17:43:32 -0800431 if (expiration == 0) {
432 // No expirations are currently configured: Cancel alarm.
433 alarmTime = 0;
434 } else if (timeToExpire <= 0) {
435 // The password has already expired: Repeat every 24 hours.
Jim Millera4e28d12010-11-08 16:15:47 -0800436 alarmTime = now + MS_PER_DAY;
Andy Stadler043116a2010-11-29 17:43:32 -0800437 } else {
438 // Selecting the next alarm time: Roll forward to the next 24 hour multiple before
439 // the expiration time.
440 long alarmInterval = timeToExpire % MS_PER_DAY;
441 if (alarmInterval == 0) {
442 alarmInterval = MS_PER_DAY;
443 }
444 alarmTime = now + alarmInterval;
Jim Millera4e28d12010-11-08 16:15:47 -0800445 }
446
Andy Stadler1f35d482010-11-19 15:39:41 -0800447 long token = Binder.clearCallingIdentity();
448 try {
449 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
450 PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_EXPIRE_PASSWORD,
451 new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
452 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
453 am.cancel(pi);
Andy Stadler043116a2010-11-29 17:43:32 -0800454 if (alarmTime != 0) {
455 am.set(AlarmManager.RTC, alarmTime, pi);
456 }
Andy Stadler1f35d482010-11-19 15:39:41 -0800457 } finally {
458 Binder.restoreCallingIdentity(token);
459 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800460 }
461
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800462 private IPowerManager getIPowerManager() {
463 if (mIPowerManager == null) {
464 IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
465 mIPowerManager = IPowerManager.Stub.asInterface(b);
466 }
467 return mIPowerManager;
468 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700469
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800470 ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800471 ActiveAdmin admin = mAdminMap.get(who);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800472 if (admin != null
473 && who.getPackageName().equals(admin.info.getActivityInfo().packageName)
474 && who.getClassName().equals(admin.info.getActivityInfo().name)) {
475 return admin;
476 }
477 return null;
478 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700479
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800480 ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
481 throws SecurityException {
Dianne Hackborn254cb442010-01-27 19:23:59 -0800482 final int callingUid = Binder.getCallingUid();
483 if (who != null) {
484 ActiveAdmin admin = mAdminMap.get(who);
485 if (admin == null) {
486 throw new SecurityException("No active admin " + who);
487 }
488 if (admin.getUid() != callingUid) {
489 throw new SecurityException("Admin " + who + " is not owned by uid "
490 + Binder.getCallingUid());
491 }
492 if (!admin.info.usesPolicy(reqPolicy)) {
493 throw new SecurityException("Admin " + admin.info.getComponent()
494 + " did not specify uses-policy for: "
495 + admin.info.getTagForPolicy(reqPolicy));
496 }
497 return admin;
498 } else {
499 final int N = mAdminList.size();
500 for (int i=0; i<N; i++) {
501 ActiveAdmin admin = mAdminList.get(i);
502 if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) {
503 return admin;
504 }
505 }
506 throw new SecurityException("No active admin owned by uid "
507 + Binder.getCallingUid() + " for policy #" + reqPolicy);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800508 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800509 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700510
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800511 void sendAdminCommandLocked(ActiveAdmin admin, String action) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800512 Intent intent = new Intent(action);
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800513 intent.setComponent(admin.info.getComponent());
Jim Millera4e28d12010-11-08 16:15:47 -0800514 if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
515 intent.putExtra("expiration", admin.passwordExpirationDate);
516 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800517 mContext.sendBroadcast(intent);
518 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700519
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800520 void sendAdminCommandLocked(String action, int reqPolicy) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800521 final int N = mAdminList.size();
522 if (N > 0) {
523 for (int i=0; i<N; i++) {
524 ActiveAdmin admin = mAdminList.get(i);
525 if (admin.info.usesPolicy(reqPolicy)) {
526 sendAdminCommandLocked(admin, action);
527 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800528 }
Dianne Hackborn4141d032010-01-21 16:29:00 -0800529 }
530 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700531
Dianne Hackbornd6847842010-01-12 18:14:19 -0800532 void removeActiveAdminLocked(ComponentName adminReceiver) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800533 ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
534 if (admin != null) {
Oscar Montemayor69238c62010-08-03 10:51:06 -0700535 boolean doProxyCleanup =
536 admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800537 sendAdminCommandLocked(admin,
Dianne Hackbornef6b22f2010-02-16 20:38:49 -0800538 DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800539 // XXX need to wait for it to complete.
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800540 mAdminList.remove(admin);
541 mAdminMap.remove(adminReceiver);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800542 validatePasswordOwnerLocked();
Oscar Montemayor69238c62010-08-03 10:51:06 -0700543 if (doProxyCleanup) {
544 resetGlobalProxy();
545 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800546 }
547 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700548
Dianne Hackbornd6847842010-01-12 18:14:19 -0800549 public DeviceAdminInfo findAdmin(ComponentName adminName) {
550 Intent resolveIntent = new Intent();
551 resolveIntent.setComponent(adminName);
552 List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
553 resolveIntent, PackageManager.GET_META_DATA);
554 if (infos == null || infos.size() <= 0) {
555 throw new IllegalArgumentException("Unknown admin: " + adminName);
556 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700557
Dianne Hackbornd6847842010-01-12 18:14:19 -0800558 try {
559 return new DeviceAdminInfo(mContext, infos.get(0));
560 } catch (XmlPullParserException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700561 Slog.w(TAG, "Bad device admin requested: " + adminName, e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800562 return null;
563 } catch (IOException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700564 Slog.w(TAG, "Bad device admin requested: " + adminName, e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800565 return null;
566 }
567 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700568
Dianne Hackbornd6847842010-01-12 18:14:19 -0800569 private static JournaledFile makeJournaledFile() {
570 final String base = "/data/system/device_policies.xml";
571 return new JournaledFile(new File(base), new File(base + ".tmp"));
572 }
573
574 private void saveSettingsLocked() {
575 JournaledFile journal = makeJournaledFile();
576 FileOutputStream stream = null;
577 try {
578 stream = new FileOutputStream(journal.chooseForWrite(), false);
579 XmlSerializer out = new FastXmlSerializer();
580 out.setOutput(stream, "utf-8");
581 out.startDocument(null, true);
582
583 out.startTag(null, "policies");
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700584
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800585 final int N = mAdminList.size();
586 for (int i=0; i<N; i++) {
587 ActiveAdmin ap = mAdminList.get(i);
588 if (ap != null) {
589 out.startTag(null, "admin");
590 out.attribute(null, "name", ap.info.getComponent().flattenToString());
591 ap.writeToXml(out);
592 out.endTag(null, "admin");
593 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800594 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700595
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800596 if (mPasswordOwner >= 0) {
597 out.startTag(null, "password-owner");
598 out.attribute(null, "value", Integer.toString(mPasswordOwner));
599 out.endTag(null, "password-owner");
600 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700601
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800602 if (mFailedPasswordAttempts != 0) {
603 out.startTag(null, "failed-password-attempts");
604 out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts));
605 out.endTag(null, "failed-password-attempts");
606 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700607
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700608 if (mActivePasswordQuality != 0 || mActivePasswordLength != 0
609 || mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0
610 || mActivePasswordLetters != 0 || mActivePasswordNumeric != 0
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700611 || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700612 out.startTag(null, "active-password");
613 out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
614 out.attribute(null, "length", Integer.toString(mActivePasswordLength));
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700615 out.attribute(null, "uppercase", Integer.toString(mActivePasswordUpperCase));
616 out.attribute(null, "lowercase", Integer.toString(mActivePasswordLowerCase));
617 out.attribute(null, "letters", Integer.toString(mActivePasswordLetters));
618 out.attribute(null, "numeric", Integer
619 .toString(mActivePasswordNumeric));
620 out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols));
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700621 out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter));
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700622 out.endTag(null, "active-password");
623 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700624
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700625 out.endTag(null, "policies");
626
Dianne Hackbornd6847842010-01-12 18:14:19 -0800627 out.endDocument();
628 stream.close();
629 journal.commit();
Jim Miller284b62e2010-06-08 14:27:42 -0700630 sendChangedNotification();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800631 } catch (IOException e) {
632 try {
633 if (stream != null) {
634 stream.close();
635 }
636 } catch (IOException ex) {
637 // Ignore
638 }
639 journal.rollback();
640 }
641 }
642
Jim Miller284b62e2010-06-08 14:27:42 -0700643 private void sendChangedNotification() {
644 Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
645 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
646 mContext.sendBroadcast(intent);
647 }
648
Dianne Hackbornd6847842010-01-12 18:14:19 -0800649 private void loadSettingsLocked() {
650 JournaledFile journal = makeJournaledFile();
651 FileInputStream stream = null;
652 File file = journal.chooseForRead();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800653 try {
654 stream = new FileInputStream(file);
655 XmlPullParser parser = Xml.newPullParser();
656 parser.setInput(stream, null);
657
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800658 int type;
659 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
660 && type != XmlPullParser.START_TAG) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800661 }
662 String tag = parser.getName();
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800663 if (!"policies".equals(tag)) {
664 throw new XmlPullParserException(
665 "Settings do not start with policies tag: found " + tag);
666 }
667 type = parser.next();
668 int outerDepth = parser.getDepth();
669 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
670 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
671 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
672 continue;
673 }
674 tag = parser.getName();
675 if ("admin".equals(tag)) {
Dianne Hackborne83cefce2010-02-04 17:38:14 -0800676 String name = parser.getAttributeValue(null, "name");
677 try {
678 DeviceAdminInfo dai = findAdmin(
679 ComponentName.unflattenFromString(name));
680 if (dai != null) {
681 ActiveAdmin ap = new ActiveAdmin(dai);
682 ap.readFromXml(parser);
683 mAdminMap.put(ap.info.getComponent(), ap);
684 mAdminList.add(ap);
685 }
686 } catch (RuntimeException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700687 Slog.w(TAG, "Failed loading admin " + name, e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800688 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800689 } else if ("failed-password-attempts".equals(tag)) {
690 mFailedPasswordAttempts = Integer.parseInt(
691 parser.getAttributeValue(null, "value"));
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800692 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800693 } else if ("password-owner".equals(tag)) {
694 mPasswordOwner = Integer.parseInt(
695 parser.getAttributeValue(null, "value"));
696 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700697 } else if ("active-password".equals(tag)) {
698 mActivePasswordQuality = Integer.parseInt(
699 parser.getAttributeValue(null, "quality"));
700 mActivePasswordLength = Integer.parseInt(
701 parser.getAttributeValue(null, "length"));
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700702 mActivePasswordUpperCase = Integer.parseInt(
703 parser.getAttributeValue(null, "uppercase"));
704 mActivePasswordLowerCase = Integer.parseInt(
705 parser.getAttributeValue(null, "lowercase"));
706 mActivePasswordLetters = Integer.parseInt(
707 parser.getAttributeValue(null, "letters"));
708 mActivePasswordNumeric = Integer.parseInt(
709 parser.getAttributeValue(null, "numeric"));
710 mActivePasswordSymbols = Integer.parseInt(
711 parser.getAttributeValue(null, "symbols"));
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700712 mActivePasswordNonLetter = Integer.parseInt(
713 parser.getAttributeValue(null, "nonletter"));
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700714 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800715 } else {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700716 Slog.w(TAG, "Unknown tag: " + tag);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800717 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800718 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800719 }
720 } catch (NullPointerException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700721 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800722 } catch (NumberFormatException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700723 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800724 } catch (XmlPullParserException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700725 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700726 } catch (FileNotFoundException e) {
727 // Don't be noisy, this is normal if we haven't defined any policies.
Dianne Hackbornd6847842010-01-12 18:14:19 -0800728 } catch (IOException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700729 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800730 } catch (IndexOutOfBoundsException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700731 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800732 }
733 try {
734 if (stream != null) {
735 stream.close();
736 }
737 } catch (IOException e) {
738 // Ignore
739 }
740
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700741 // Validate that what we stored for the password quality matches
742 // sufficiently what is currently set. Note that this is only
743 // a sanity check in case the two get out of sync; this should
744 // never normally happen.
745 LockPatternUtils utils = new LockPatternUtils(mContext);
746 if (utils.getActivePasswordQuality() < mActivePasswordQuality) {
747 Slog.w(TAG, "Active password quality 0x"
748 + Integer.toHexString(mActivePasswordQuality)
749 + " does not match actual quality 0x"
750 + Integer.toHexString(utils.getActivePasswordQuality()));
751 mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
752 mActivePasswordLength = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700753 mActivePasswordUpperCase = 0;
754 mActivePasswordLowerCase = 0;
755 mActivePasswordLetters = 0;
756 mActivePasswordNumeric = 0;
757 mActivePasswordSymbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700758 mActivePasswordNonLetter = 0;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700759 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700760
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800761 validatePasswordOwnerLocked();
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700762
Dianne Hackborn254cb442010-01-27 19:23:59 -0800763 long timeMs = getMaximumTimeToLock(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800764 if (timeMs <= 0) {
765 timeMs = Integer.MAX_VALUE;
766 }
767 try {
768 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
769 } catch (RemoteException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700770 Slog.w(TAG, "Failure talking with power manager", e);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800771 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800772 }
773
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700774 static void validateQualityConstant(int quality) {
775 switch (quality) {
776 case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
777 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
778 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
779 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
780 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700781 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700782 return;
783 }
784 throw new IllegalArgumentException("Invalid quality constant: 0x"
785 + Integer.toHexString(quality));
786 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700787
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800788 void validatePasswordOwnerLocked() {
789 if (mPasswordOwner >= 0) {
790 boolean haveOwner = false;
791 for (int i=mAdminList.size()-1; i>=0; i--) {
792 if (mAdminList.get(i).getUid() == mPasswordOwner) {
793 haveOwner = true;
794 break;
795 }
796 }
797 if (!haveOwner) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700798 Slog.w(TAG, "Previous password owner " + mPasswordOwner
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800799 + " no longer active; disabling");
800 mPasswordOwner = -1;
801 }
802 }
803 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700804
Dianne Hackbornd6847842010-01-12 18:14:19 -0800805 public void systemReady() {
806 synchronized (this) {
807 loadSettingsLocked();
808 }
809 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700810
Jim Millera4e28d12010-11-08 16:15:47 -0800811 private void handlePasswordExpirationNotification() {
812 synchronized (this) {
813 final long now = System.currentTimeMillis();
814 final int N = mAdminList.size();
815 if (N <= 0) {
816 return;
817 }
818 for (int i=0; i < N; i++) {
819 ActiveAdmin admin = mAdminList.get(i);
820 if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
821 && admin.passwordExpirationTimeout > 0L
822 && admin.passwordExpirationDate > 0L
Andy Stadler043116a2010-11-29 17:43:32 -0800823 && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
Jim Millera4e28d12010-11-08 16:15:47 -0800824 sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
825 }
826 }
827 setExpirationAlarmCheckLocked(mContext);
828 }
829 }
830
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800831 /**
832 * @param adminReceiver The admin to add
833 * @param refreshing true = update an active admin, no error
834 */
835 public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800836 mContext.enforceCallingOrSelfPermission(
837 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700838
Dianne Hackbornd6847842010-01-12 18:14:19 -0800839 DeviceAdminInfo info = findAdmin(adminReceiver);
840 if (info == null) {
841 throw new IllegalArgumentException("Bad admin: " + adminReceiver);
842 }
843 synchronized (this) {
844 long ident = Binder.clearCallingIdentity();
845 try {
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800846 if (!refreshing && getActiveAdminUncheckedLocked(adminReceiver) != null) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800847 throw new IllegalArgumentException("Admin is already added");
Dianne Hackbornd6847842010-01-12 18:14:19 -0800848 }
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800849 ActiveAdmin newAdmin = new ActiveAdmin(info);
850 mAdminMap.put(adminReceiver, newAdmin);
851 int replaceIndex = -1;
852 if (refreshing) {
853 final int N = mAdminList.size();
854 for (int i=0; i < N; i++) {
855 ActiveAdmin oldAdmin = mAdminList.get(i);
856 if (oldAdmin.info.getComponent().equals(adminReceiver)) {
857 replaceIndex = i;
858 break;
859 }
860 }
861 }
862 if (replaceIndex == -1) {
863 mAdminList.add(newAdmin);
864 } else {
865 mAdminList.set(replaceIndex, newAdmin);
866 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800867 saveSettingsLocked();
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800868 sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800869 } finally {
870 Binder.restoreCallingIdentity(ident);
871 }
872 }
873 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700874
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800875 public boolean isAdminActive(ComponentName adminReceiver) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800876 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800877 return getActiveAdminUncheckedLocked(adminReceiver) != null;
878 }
879 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700880
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800881 public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId) {
882 synchronized (this) {
883 ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver);
884 if (administrator == null) {
885 throw new SecurityException("No active admin " + adminReceiver);
886 }
887 return administrator.info.usesPolicy(policyId);
888 }
889 }
890
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800891 public List<ComponentName> getActiveAdmins() {
892 synchronized (this) {
893 final int N = mAdminList.size();
894 if (N <= 0) {
895 return null;
896 }
897 ArrayList<ComponentName> res = new ArrayList<ComponentName>(N);
898 for (int i=0; i<N; i++) {
899 res.add(mAdminList.get(i).info.getComponent());
900 }
901 return res;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800902 }
903 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700904
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800905 public boolean packageHasActiveAdmins(String packageName) {
906 synchronized (this) {
907 final int N = mAdminList.size();
908 for (int i=0; i<N; i++) {
909 if (mAdminList.get(i).info.getPackageName().equals(packageName)) {
910 return true;
911 }
912 }
913 return false;
914 }
915 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700916
Dianne Hackbornd6847842010-01-12 18:14:19 -0800917 public void removeActiveAdmin(ComponentName adminReceiver) {
918 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800919 ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
920 if (admin == null) {
921 return;
922 }
923 if (admin.getUid() != Binder.getCallingUid()) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800924 mContext.enforceCallingOrSelfPermission(
925 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
926 }
927 long ident = Binder.clearCallingIdentity();
928 try {
929 removeActiveAdminLocked(adminReceiver);
930 } finally {
931 Binder.restoreCallingIdentity(ident);
932 }
933 }
934 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700935
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700936 public void setPasswordQuality(ComponentName who, int quality) {
937 validateQualityConstant(quality);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700938
Dianne Hackbornd6847842010-01-12 18:14:19 -0800939 synchronized (this) {
940 if (who == null) {
941 throw new NullPointerException("ComponentName is null");
942 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800943 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
944 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700945 if (ap.passwordQuality != quality) {
946 ap.passwordQuality = quality;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800947 saveSettingsLocked();
948 }
949 }
950 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700951
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800952 public int getPasswordQuality(ComponentName who) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800953 synchronized (this) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800954 int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700955
Dianne Hackborn254cb442010-01-27 19:23:59 -0800956 if (who != null) {
957 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800958 return admin != null ? admin.passwordQuality : mode;
Dianne Hackborn254cb442010-01-27 19:23:59 -0800959 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700960
Dianne Hackborn254cb442010-01-27 19:23:59 -0800961 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800962 for (int i=0; i<N; i++) {
963 ActiveAdmin admin = mAdminList.get(i);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800964 if (mode < admin.passwordQuality) {
965 mode = admin.passwordQuality;
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800966 }
967 }
968 return mode;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800969 }
970 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700971
Dianne Hackborn254cb442010-01-27 19:23:59 -0800972 public void setPasswordMinimumLength(ComponentName who, int length) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800973 synchronized (this) {
974 if (who == null) {
975 throw new NullPointerException("ComponentName is null");
976 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800977 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
978 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800979 if (ap.minimumPasswordLength != length) {
980 ap.minimumPasswordLength = length;
981 saveSettingsLocked();
982 }
983 }
984 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700985
Dianne Hackborn254cb442010-01-27 19:23:59 -0800986 public int getPasswordMinimumLength(ComponentName who) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800987 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800988 int length = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700989
Dianne Hackborn254cb442010-01-27 19:23:59 -0800990 if (who != null) {
991 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
992 return admin != null ? admin.minimumPasswordLength : length;
993 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700994
Dianne Hackborn254cb442010-01-27 19:23:59 -0800995 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800996 for (int i=0; i<N; i++) {
997 ActiveAdmin admin = mAdminList.get(i);
998 if (length < admin.minimumPasswordLength) {
999 length = admin.minimumPasswordLength;
1000 }
1001 }
1002 return length;
Dianne Hackbornd6847842010-01-12 18:14:19 -08001003 }
1004 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001005
1006 public void setPasswordHistoryLength(ComponentName who, int length) {
1007 synchronized (this) {
1008 if (who == null) {
1009 throw new NullPointerException("ComponentName is null");
1010 }
1011 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1012 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1013 if (ap.passwordHistoryLength != length) {
1014 ap.passwordHistoryLength = length;
1015 saveSettingsLocked();
1016 }
1017 }
1018 }
1019
1020 public int getPasswordHistoryLength(ComponentName who) {
1021 synchronized (this) {
1022 int length = 0;
1023
1024 if (who != null) {
1025 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1026 return admin != null ? admin.passwordHistoryLength : length;
1027 }
1028
1029 final int N = mAdminList.size();
1030 for (int i = 0; i < N; i++) {
1031 ActiveAdmin admin = mAdminList.get(i);
1032 if (length < admin.passwordHistoryLength) {
1033 length = admin.passwordHistoryLength;
1034 }
1035 }
1036 return length;
1037 }
1038 }
1039
Jim Millera4e28d12010-11-08 16:15:47 -08001040 public void setPasswordExpirationTimeout(ComponentName who, long timeout) {
1041 synchronized (this) {
1042 if (who == null) {
1043 throw new NullPointerException("ComponentName is null");
1044 }
Andy Stadler1f35d482010-11-19 15:39:41 -08001045 if (timeout < 0) {
1046 throw new IllegalArgumentException("Timeout must be >= 0 ms");
Jim Millera4e28d12010-11-08 16:15:47 -08001047 }
1048 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1049 DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
1050 // Calling this API automatically bumps the expiration date
1051 final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
1052 ap.passwordExpirationDate = expiration;
1053 ap.passwordExpirationTimeout = timeout;
1054 if (timeout > 0L) {
1055 Slog.w(TAG, "setPasswordExpiration(): password will expire on "
1056 + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)
1057 .format(new Date(expiration)));
1058 }
1059 saveSettingsLocked();
1060 setExpirationAlarmCheckLocked(mContext); // in case this is the first one
1061 }
1062 }
1063
Andy Stadler043116a2010-11-29 17:43:32 -08001064 /**
1065 * Return a single admin's expiration cycle time, or the min of all cycle times.
1066 * Returns 0 if not configured.
1067 */
Jim Millera4e28d12010-11-08 16:15:47 -08001068 public long getPasswordExpirationTimeout(ComponentName who) {
1069 synchronized (this) {
Jim Millera4e28d12010-11-08 16:15:47 -08001070 if (who != null) {
1071 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
Andy Stadler043116a2010-11-29 17:43:32 -08001072 return admin != null ? admin.passwordExpirationTimeout : 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001073 }
1074
Andy Stadler043116a2010-11-29 17:43:32 -08001075 long timeout = 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001076 final int N = mAdminList.size();
1077 for (int i = 0; i < N; i++) {
1078 ActiveAdmin admin = mAdminList.get(i);
1079 if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
1080 && timeout > admin.passwordExpirationTimeout)) {
1081 timeout = admin.passwordExpirationTimeout;
1082 }
1083 }
1084 return timeout;
1085 }
1086 }
1087
Andy Stadler043116a2010-11-29 17:43:32 -08001088 /**
1089 * Return a single admin's expiration date/time, or the min (soonest) for all admins.
1090 * Returns 0 if not configured.
1091 */
Jim Millera4e28d12010-11-08 16:15:47 -08001092 private long getPasswordExpirationLocked(ComponentName who) {
Jim Millera4e28d12010-11-08 16:15:47 -08001093 if (who != null) {
1094 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
Andy Stadler043116a2010-11-29 17:43:32 -08001095 return admin != null ? admin.passwordExpirationDate : 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001096 }
1097
Andy Stadler043116a2010-11-29 17:43:32 -08001098 long timeout = 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001099 final int N = mAdminList.size();
1100 for (int i = 0; i < N; i++) {
1101 ActiveAdmin admin = mAdminList.get(i);
1102 if (timeout == 0L || (admin.passwordExpirationDate != 0
1103 && timeout > admin.passwordExpirationDate)) {
1104 timeout = admin.passwordExpirationDate;
1105 }
1106 }
1107 return timeout;
1108 }
1109
1110 public long getPasswordExpiration(ComponentName who) {
1111 synchronized (this) {
1112 return getPasswordExpirationLocked(who);
1113 }
1114 }
1115
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001116 public void setPasswordMinimumUpperCase(ComponentName who, int length) {
1117 synchronized (this) {
1118 if (who == null) {
1119 throw new NullPointerException("ComponentName is null");
1120 }
1121 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1122 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1123 if (ap.minimumPasswordUpperCase != length) {
1124 ap.minimumPasswordUpperCase = length;
1125 saveSettingsLocked();
1126 }
1127 }
1128 }
1129
1130 public int getPasswordMinimumUpperCase(ComponentName who) {
1131 synchronized (this) {
1132 int length = 0;
1133
1134 if (who != null) {
1135 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1136 return admin != null ? admin.minimumPasswordUpperCase : length;
1137 }
1138
1139 final int N = mAdminList.size();
1140 for (int i=0; i<N; i++) {
1141 ActiveAdmin admin = mAdminList.get(i);
1142 if (length < admin.minimumPasswordUpperCase) {
1143 length = admin.minimumPasswordUpperCase;
1144 }
1145 }
1146 return length;
1147 }
1148 }
1149
1150 public void setPasswordMinimumLowerCase(ComponentName who, int length) {
1151 synchronized (this) {
1152 if (who == null) {
1153 throw new NullPointerException("ComponentName is null");
1154 }
1155 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1156 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1157 if (ap.minimumPasswordLowerCase != length) {
1158 ap.minimumPasswordLowerCase = length;
1159 saveSettingsLocked();
1160 }
1161 }
1162 }
1163
1164 public int getPasswordMinimumLowerCase(ComponentName who) {
1165 synchronized (this) {
1166 int length = 0;
1167
1168 if (who != null) {
1169 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1170 return admin != null ? admin.minimumPasswordLowerCase : length;
1171 }
1172
1173 final int N = mAdminList.size();
1174 for (int i=0; i<N; i++) {
1175 ActiveAdmin admin = mAdminList.get(i);
1176 if (length < admin.minimumPasswordLowerCase) {
1177 length = admin.minimumPasswordLowerCase;
1178 }
1179 }
1180 return length;
1181 }
1182 }
1183
1184 public void setPasswordMinimumLetters(ComponentName who, int length) {
1185 synchronized (this) {
1186 if (who == null) {
1187 throw new NullPointerException("ComponentName is null");
1188 }
1189 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1190 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1191 if (ap.minimumPasswordLetters != length) {
1192 ap.minimumPasswordLetters = length;
1193 saveSettingsLocked();
1194 }
1195 }
1196 }
1197
1198 public int getPasswordMinimumLetters(ComponentName who) {
1199 synchronized (this) {
1200 int length = 0;
1201
1202 if (who != null) {
1203 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1204 return admin != null ? admin.minimumPasswordLetters : length;
1205 }
1206
1207 final int N = mAdminList.size();
1208 for (int i=0; i<N; i++) {
1209 ActiveAdmin admin = mAdminList.get(i);
1210 if (length < admin.minimumPasswordLetters) {
1211 length = admin.minimumPasswordLetters;
1212 }
1213 }
1214 return length;
1215 }
1216 }
1217
1218 public void setPasswordMinimumNumeric(ComponentName who, int length) {
1219 synchronized (this) {
1220 if (who == null) {
1221 throw new NullPointerException("ComponentName is null");
1222 }
1223 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1224 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1225 if (ap.minimumPasswordNumeric != length) {
1226 ap.minimumPasswordNumeric = length;
1227 saveSettingsLocked();
1228 }
1229 }
1230 }
1231
1232 public int getPasswordMinimumNumeric(ComponentName who) {
1233 synchronized (this) {
1234 int length = 0;
1235
1236 if (who != null) {
1237 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1238 return admin != null ? admin.minimumPasswordNumeric : length;
1239 }
1240
1241 final int N = mAdminList.size();
1242 for (int i = 0; i < N; i++) {
1243 ActiveAdmin admin = mAdminList.get(i);
1244 if (length < admin.minimumPasswordNumeric) {
1245 length = admin.minimumPasswordNumeric;
1246 }
1247 }
1248 return length;
1249 }
1250 }
1251
1252 public void setPasswordMinimumSymbols(ComponentName who, int length) {
1253 synchronized (this) {
1254 if (who == null) {
1255 throw new NullPointerException("ComponentName is null");
1256 }
1257 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1258 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1259 if (ap.minimumPasswordSymbols != length) {
1260 ap.minimumPasswordSymbols = length;
1261 saveSettingsLocked();
1262 }
1263 }
1264 }
1265
1266 public int getPasswordMinimumSymbols(ComponentName who) {
1267 synchronized (this) {
1268 int length = 0;
1269
1270 if (who != null) {
1271 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1272 return admin != null ? admin.minimumPasswordSymbols : length;
1273 }
1274
1275 final int N = mAdminList.size();
1276 for (int i=0; i<N; i++) {
1277 ActiveAdmin admin = mAdminList.get(i);
1278 if (length < admin.minimumPasswordSymbols) {
1279 length = admin.minimumPasswordSymbols;
1280 }
1281 }
1282 return length;
1283 }
1284 }
1285
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001286 public void setPasswordMinimumNonLetter(ComponentName who, int length) {
1287 synchronized (this) {
1288 if (who == null) {
1289 throw new NullPointerException("ComponentName is null");
1290 }
1291 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1292 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1293 if (ap.minimumPasswordNonLetter != length) {
1294 ap.minimumPasswordNonLetter = length;
1295 saveSettingsLocked();
1296 }
1297 }
1298 }
1299
1300 public int getPasswordMinimumNonLetter(ComponentName who) {
1301 synchronized (this) {
1302 int length = 0;
1303
1304 if (who != null) {
1305 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1306 return admin != null ? admin.minimumPasswordNonLetter : length;
1307 }
1308
1309 final int N = mAdminList.size();
1310 for (int i=0; i<N; i++) {
1311 ActiveAdmin admin = mAdminList.get(i);
1312 if (length < admin.minimumPasswordNonLetter) {
1313 length = admin.minimumPasswordNonLetter;
1314 }
1315 }
1316 return length;
1317 }
1318 }
1319
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001320 public boolean isActivePasswordSufficient() {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001321 synchronized (this) {
1322 // This API can only be called by an active device admin,
1323 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001324 getActiveAdminForCallerLocked(null,
1325 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001326 if (mActivePasswordQuality < getPasswordQuality(null)
1327 || mActivePasswordLength < getPasswordMinimumLength(null)) {
1328 return false;
1329 }
1330 if(mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
1331 return true;
1332 }
1333 return mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null)
1334 && mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null)
1335 && mActivePasswordLetters >= getPasswordMinimumLetters(null)
1336 && mActivePasswordNumeric >= getPasswordMinimumNumeric(null)
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001337 && mActivePasswordSymbols >= getPasswordMinimumSymbols(null)
1338 && mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001339 }
1340 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001341
Dianne Hackbornd6847842010-01-12 18:14:19 -08001342 public int getCurrentFailedPasswordAttempts() {
1343 synchronized (this) {
1344 // This API can only be called by an active device admin,
1345 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001346 getActiveAdminForCallerLocked(null,
1347 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001348 return mFailedPasswordAttempts;
1349 }
1350 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001351
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001352 public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
1353 synchronized (this) {
1354 // This API can only be called by an active device admin,
1355 // so try to retrieve it to check that the caller is one.
1356 getActiveAdminForCallerLocked(who,
1357 DeviceAdminInfo.USES_POLICY_WIPE_DATA);
1358 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1359 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
1360 if (ap.maximumFailedPasswordsForWipe != num) {
1361 ap.maximumFailedPasswordsForWipe = num;
1362 saveSettingsLocked();
1363 }
1364 }
1365 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001366
Dianne Hackborn254cb442010-01-27 19:23:59 -08001367 public int getMaximumFailedPasswordsForWipe(ComponentName who) {
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001368 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001369 int count = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001370
Dianne Hackborn254cb442010-01-27 19:23:59 -08001371 if (who != null) {
1372 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1373 return admin != null ? admin.maximumFailedPasswordsForWipe : count;
1374 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001375
Dianne Hackborn254cb442010-01-27 19:23:59 -08001376 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001377 for (int i=0; i<N; i++) {
1378 ActiveAdmin admin = mAdminList.get(i);
1379 if (count == 0) {
1380 count = admin.maximumFailedPasswordsForWipe;
1381 } else if (admin.maximumFailedPasswordsForWipe != 0
1382 && count > admin.maximumFailedPasswordsForWipe) {
1383 count = admin.maximumFailedPasswordsForWipe;
1384 }
1385 }
1386 return count;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001387 }
1388 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001389
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001390 public boolean resetPassword(String password, int flags) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001391 int quality;
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001392 synchronized (this) {
1393 // This API can only be called by an active device admin,
1394 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001395 getActiveAdminForCallerLocked(null,
1396 DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001397 quality = getPasswordQuality(null);
1398 if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001399 int realQuality = LockPatternUtils.computePasswordQuality(password);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001400 if (realQuality < quality
1401 && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001402 Slog.w(TAG, "resetPassword: password quality 0x"
1403 + Integer.toHexString(quality)
1404 + " does not meet required quality 0x"
1405 + Integer.toHexString(quality));
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001406 return false;
1407 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001408 quality = Math.max(realQuality, quality);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001409 }
1410 int length = getPasswordMinimumLength(null);
1411 if (password.length() < length) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001412 Slog.w(TAG, "resetPassword: password length " + password.length()
1413 + " does not meet required length " + length);
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001414 return false;
1415 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001416 if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
1417 int letters = 0;
1418 int uppercase = 0;
1419 int lowercase = 0;
1420 int numbers = 0;
1421 int symbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001422 int nonletter = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001423 for (int i = 0; i < password.length(); i++) {
1424 char c = password.charAt(i);
1425 if (c >= 'A' && c <= 'Z') {
1426 letters++;
1427 uppercase++;
1428 } else if (c >= 'a' && c <= 'z') {
1429 letters++;
1430 lowercase++;
1431 } else if (c >= '0' && c <= '9') {
1432 numbers++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001433 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001434 } else {
1435 symbols++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001436 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001437 }
1438 }
1439 int neededLetters = getPasswordMinimumLetters(null);
1440 if(letters < neededLetters) {
1441 Slog.w(TAG, "resetPassword: number of letters " + letters
1442 + " does not meet required number of letters " + neededLetters);
1443 return false;
1444 }
1445 int neededNumbers = getPasswordMinimumNumeric(null);
1446 if (numbers < neededNumbers) {
1447 Slog
1448 .w(TAG, "resetPassword: number of numerical digits " + numbers
1449 + " does not meet required number of numerical digits "
1450 + neededNumbers);
1451 return false;
1452 }
1453 int neededLowerCase = getPasswordMinimumLowerCase(null);
1454 if (lowercase < neededLowerCase) {
1455 Slog.w(TAG, "resetPassword: number of lowercase letters " + lowercase
1456 + " does not meet required number of lowercase letters "
1457 + neededLowerCase);
1458 return false;
1459 }
1460 int neededUpperCase = getPasswordMinimumUpperCase(null);
1461 if (uppercase < neededUpperCase) {
1462 Slog.w(TAG, "resetPassword: number of uppercase letters " + uppercase
1463 + " does not meet required number of uppercase letters "
1464 + neededUpperCase);
1465 return false;
1466 }
1467 int neededSymbols = getPasswordMinimumSymbols(null);
1468 if (symbols < neededSymbols) {
1469 Slog.w(TAG, "resetPassword: number of special symbols " + symbols
1470 + " does not meet required number of special symbols " + neededSymbols);
1471 return false;
1472 }
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001473 int neededNonLetter = getPasswordMinimumNonLetter(null);
1474 if (nonletter < neededNonLetter) {
1475 Slog.w(TAG, "resetPassword: number of non-letter characters " + nonletter
1476 + " does not meet required number of non-letter characters "
1477 + neededNonLetter);
1478 return false;
1479 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001480 }
1481
1482 LockPatternUtils utils = new LockPatternUtils(mContext);
1483 if(utils.checkPasswordHistory(password)) {
1484 Slog.w(TAG, "resetPassword: password is the same as one of the last "
1485 + getPasswordHistoryLength(null) + " passwords");
1486 return false;
1487 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001488 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001489
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001490 int callingUid = Binder.getCallingUid();
1491 if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001492 Slog.w(TAG, "resetPassword: already set by another uid and not entered by user");
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001493 return false;
1494 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001495
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001496 // Don't do this with the lock held, because it is going to call
1497 // back in to the service.
1498 long ident = Binder.clearCallingIdentity();
1499 try {
1500 LockPatternUtils utils = new LockPatternUtils(mContext);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001501 utils.saveLockPassword(password, quality);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001502 synchronized (this) {
1503 int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
1504 != 0 ? callingUid : -1;
1505 if (mPasswordOwner != newOwner) {
1506 mPasswordOwner = newOwner;
1507 saveSettingsLocked();
1508 }
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001509 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001510 } finally {
1511 Binder.restoreCallingIdentity(ident);
1512 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001513
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001514 return true;
1515 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001516
Dianne Hackbornd6847842010-01-12 18:14:19 -08001517 public void setMaximumTimeToLock(ComponentName who, long timeMs) {
1518 synchronized (this) {
1519 if (who == null) {
1520 throw new NullPointerException("ComponentName is null");
1521 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001522 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
Dianne Hackborn315ada72010-02-11 12:14:08 -08001523 DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001524 if (ap.maximumTimeToUnlock != timeMs) {
1525 ap.maximumTimeToUnlock = timeMs;
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001526
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001527 long ident = Binder.clearCallingIdentity();
1528 try {
1529 saveSettingsLocked();
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001530
Dianne Hackborn254cb442010-01-27 19:23:59 -08001531 timeMs = getMaximumTimeToLock(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001532 if (timeMs <= 0) {
1533 timeMs = Integer.MAX_VALUE;
1534 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001535
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001536 try {
1537 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
1538 } catch (RemoteException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001539 Slog.w(TAG, "Failure talking with power manager", e);
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001540 }
1541 } finally {
1542 Binder.restoreCallingIdentity(ident);
1543 }
Dianne Hackbornd6847842010-01-12 18:14:19 -08001544 }
1545 }
1546 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001547
Dianne Hackborn254cb442010-01-27 19:23:59 -08001548 public long getMaximumTimeToLock(ComponentName who) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001549 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001550 long time = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001551
Dianne Hackborn254cb442010-01-27 19:23:59 -08001552 if (who != null) {
1553 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1554 return admin != null ? admin.maximumTimeToUnlock : time;
1555 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001556
Dianne Hackborn254cb442010-01-27 19:23:59 -08001557 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001558 for (int i=0; i<N; i++) {
1559 ActiveAdmin admin = mAdminList.get(i);
1560 if (time == 0) {
1561 time = admin.maximumTimeToUnlock;
1562 } else if (admin.maximumTimeToUnlock != 0
1563 && time > admin.maximumTimeToUnlock) {
1564 time = admin.maximumTimeToUnlock;
1565 }
1566 }
1567 return time;
Dianne Hackbornd6847842010-01-12 18:14:19 -08001568 }
1569 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001570
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001571 public void lockNow() {
1572 synchronized (this) {
1573 // This API can only be called by an active device admin,
1574 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001575 getActiveAdminForCallerLocked(null,
1576 DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
Dianne Hackborn254cb442010-01-27 19:23:59 -08001577 long ident = Binder.clearCallingIdentity();
1578 try {
1579 mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(),
1580 WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN);
1581 } catch (RemoteException e) {
1582 } finally {
1583 Binder.restoreCallingIdentity(ident);
1584 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001585 }
1586 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001587
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001588 void wipeDataLocked(int flags) {
Dianne Hackborn42499172010-10-15 18:45:07 -07001589 if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) {
1590 Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
1591 intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
1592 mWakeLock.acquire(10000);
1593 mContext.startService(intent);
1594 } else {
1595 try {
1596 RecoverySystem.rebootWipeUserData(mContext);
1597 } catch (IOException e) {
1598 Slog.w(TAG, "Failed requesting data wipe", e);
1599 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001600 }
1601 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001602
Dianne Hackbornd6847842010-01-12 18:14:19 -08001603 public void wipeData(int flags) {
1604 synchronized (this) {
1605 // This API can only be called by an active device admin,
1606 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001607 getActiveAdminForCallerLocked(null,
1608 DeviceAdminInfo.USES_POLICY_WIPE_DATA);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001609 long ident = Binder.clearCallingIdentity();
1610 try {
1611 wipeDataLocked(flags);
1612 } finally {
1613 Binder.restoreCallingIdentity(ident);
1614 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001615 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001616 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001617
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001618 public void getRemoveWarning(ComponentName comp, final RemoteCallback result) {
1619 mContext.enforceCallingOrSelfPermission(
1620 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001621
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001622 synchronized (this) {
1623 ActiveAdmin admin = getActiveAdminUncheckedLocked(comp);
1624 if (admin == null) {
1625 try {
1626 result.sendResult(null);
1627 } catch (RemoteException e) {
1628 }
1629 return;
1630 }
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001631 Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001632 intent.setComponent(admin.info.getComponent());
1633 mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
1634 @Override
1635 public void onReceive(Context context, Intent intent) {
1636 try {
1637 result.sendResult(getResultExtras(false));
1638 } catch (RemoteException e) {
1639 }
1640 }
1641 }, null, Activity.RESULT_OK, null, null);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001642 }
1643 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001644
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001645 public void setActivePasswordState(int quality, int length, int letters, int uppercase,
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001646 int lowercase, int numbers, int symbols, int nonletter) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001647 mContext.enforceCallingOrSelfPermission(
1648 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001649
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001650 validateQualityConstant(quality);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001651
Dianne Hackbornd6847842010-01-12 18:14:19 -08001652 synchronized (this) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001653 if (mActivePasswordQuality != quality || mActivePasswordLength != length
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001654 || mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters
1655 || mActivePasswordUpperCase != uppercase
1656 || mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001657 || mActivePasswordSymbols != symbols || mActivePasswordNonLetter != nonletter) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001658 long ident = Binder.clearCallingIdentity();
1659 try {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001660 mActivePasswordQuality = quality;
Dianne Hackbornd6847842010-01-12 18:14:19 -08001661 mActivePasswordLength = length;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001662 mActivePasswordLetters = letters;
1663 mActivePasswordLowerCase = lowercase;
1664 mActivePasswordUpperCase = uppercase;
1665 mActivePasswordNumeric = numbers;
1666 mActivePasswordSymbols = symbols;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001667 mActivePasswordNonLetter = nonletter;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001668 mFailedPasswordAttempts = 0;
1669 saveSettingsLocked();
Jim Millera4e28d12010-11-08 16:15:47 -08001670 updatePasswordExpirationsLocked();
Andy Stadler043116a2010-11-29 17:43:32 -08001671 setExpirationAlarmCheckLocked(mContext);
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001672 sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001673 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001674 } finally {
1675 Binder.restoreCallingIdentity(ident);
1676 }
1677 }
1678 }
1679 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001680
Andy Stadler043116a2010-11-29 17:43:32 -08001681 /**
1682 * Called any time the device password is updated. Resets all password expiration clocks.
1683 */
Jim Millera4e28d12010-11-08 16:15:47 -08001684 private void updatePasswordExpirationsLocked() {
1685 final int N = mAdminList.size();
1686 if (N > 0) {
1687 for (int i=0; i<N; i++) {
1688 ActiveAdmin admin = mAdminList.get(i);
1689 if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
Andy Stadler043116a2010-11-29 17:43:32 -08001690 long timeout = admin.passwordExpirationTimeout;
1691 long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
1692 admin.passwordExpirationDate = expiration;
Jim Millera4e28d12010-11-08 16:15:47 -08001693 }
1694 }
1695 saveSettingsLocked();
1696 }
1697 }
1698
Dianne Hackbornd6847842010-01-12 18:14:19 -08001699 public void reportFailedPasswordAttempt() {
1700 mContext.enforceCallingOrSelfPermission(
1701 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001702
Dianne Hackbornd6847842010-01-12 18:14:19 -08001703 synchronized (this) {
1704 long ident = Binder.clearCallingIdentity();
1705 try {
1706 mFailedPasswordAttempts++;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001707 saveSettingsLocked();
Dianne Hackborn254cb442010-01-27 19:23:59 -08001708 int max = getMaximumFailedPasswordsForWipe(null);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001709 if (max > 0 && mFailedPasswordAttempts >= max) {
1710 wipeDataLocked(0);
1711 }
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001712 sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001713 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001714 } finally {
1715 Binder.restoreCallingIdentity(ident);
1716 }
1717 }
1718 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001719
Dianne Hackbornd6847842010-01-12 18:14:19 -08001720 public void reportSuccessfulPasswordAttempt() {
1721 mContext.enforceCallingOrSelfPermission(
1722 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001723
Dianne Hackbornd6847842010-01-12 18:14:19 -08001724 synchronized (this) {
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001725 if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001726 long ident = Binder.clearCallingIdentity();
1727 try {
1728 mFailedPasswordAttempts = 0;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001729 mPasswordOwner = -1;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001730 saveSettingsLocked();
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001731 sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001732 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001733 } finally {
1734 Binder.restoreCallingIdentity(ident);
1735 }
1736 }
1737 }
1738 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001739
Oscar Montemayor69238c62010-08-03 10:51:06 -07001740 public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
1741 String exclusionList) {
1742 synchronized(this) {
1743 if (who == null) {
1744 throw new NullPointerException("ComponentName is null");
1745 }
1746
1747 ActiveAdmin admin = getActiveAdminForCallerLocked(who,
1748 DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
1749
1750 // Scan through active admins and find if anyone has already
1751 // set the global proxy.
1752 final int N = mAdminList.size();
1753 Set<ComponentName> compSet = mAdminMap.keySet();
1754 for (ComponentName component : compSet) {
1755 ActiveAdmin ap = mAdminMap.get(component);
1756 if ((ap.specifiesGlobalProxy) && (!component.equals(who))) {
1757 // Another admin already sets the global proxy
1758 // Return it to the caller.
1759 return component;
1760 }
1761 }
1762 if (proxySpec == null) {
1763 admin.specifiesGlobalProxy = false;
1764 admin.globalProxySpec = null;
1765 admin.globalProxyExclusionList = null;
1766 } else {
1767
1768 admin.specifiesGlobalProxy = true;
1769 admin.globalProxySpec = proxySpec;
1770 admin.globalProxyExclusionList = exclusionList;
1771 }
1772
1773 // Reset the global proxy accordingly
1774 // Do this using system permissions, as apps cannot write to secure settings
1775 long origId = Binder.clearCallingIdentity();
1776 resetGlobalProxy();
1777 Binder.restoreCallingIdentity(origId);
1778 return null;
1779 }
1780 }
1781
1782 public ComponentName getGlobalProxyAdmin() {
1783 synchronized(this) {
1784 // Scan through active admins and find if anyone has already
1785 // set the global proxy.
1786 final int N = mAdminList.size();
1787 for (int i = 0; i < N; i++) {
1788 ActiveAdmin ap = mAdminList.get(i);
1789 if (ap.specifiesGlobalProxy) {
1790 // Device admin sets the global proxy
1791 // Return it to the caller.
1792 return ap.info.getComponent();
1793 }
1794 }
1795 }
1796 // No device admin sets the global proxy.
1797 return null;
1798 }
1799
1800 private void resetGlobalProxy() {
1801 final int N = mAdminList.size();
1802 for (int i = 0; i < N; i++) {
1803 ActiveAdmin ap = mAdminList.get(i);
1804 if (ap.specifiesGlobalProxy) {
1805 saveGlobalProxy(ap.globalProxySpec, ap.globalProxyExclusionList);
1806 return;
1807 }
1808 }
1809 // No device admins defining global proxies - reset global proxy settings to none
1810 saveGlobalProxy(null, null);
1811 }
1812
1813 private void saveGlobalProxy(String proxySpec, String exclusionList) {
1814 if (exclusionList == null) {
1815 exclusionList = "";
1816 }
1817 if (proxySpec == null) {
1818 proxySpec = "";
1819 }
1820 // Remove white spaces
1821 proxySpec = proxySpec.trim();
Robert Greenwalt434203a2010-10-11 16:00:27 -07001822 String data[] = proxySpec.split(":");
1823 int proxyPort = 8080;
1824 if (data.length > 1) {
1825 try {
1826 proxyPort = Integer.parseInt(data[1]);
1827 } catch (NumberFormatException e) {}
1828 }
Oscar Montemayor69238c62010-08-03 10:51:06 -07001829 exclusionList = exclusionList.trim();
1830 ContentResolver res = mContext.getContentResolver();
Robert Greenwalt434203a2010-10-11 16:00:27 -07001831 Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, data[0]);
1832 Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, proxyPort);
1833 Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
1834 exclusionList);
Oscar Montemayor69238c62010-08-03 10:51:06 -07001835 }
1836
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001837 /**
Andy Stadler22dbfda2011-01-17 12:47:31 -08001838 * Set the storage encryption request for a single admin. Returns the new total request
1839 * status (for all admins).
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001840 */
1841 public int setStorageEncryption(ComponentName who, boolean encrypt) {
1842 synchronized (this) {
1843 // Check for permissions
1844 if (who == null) {
1845 throw new NullPointerException("ComponentName is null");
1846 }
1847 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1848 DeviceAdminInfo.USES_ENCRYPTED_STORAGE);
1849
Andy Stadler22dbfda2011-01-17 12:47:31 -08001850 // Quick exit: If the filesystem does not support encryption, we can exit early.
1851 if (!isEncryptionSupported()) {
1852 return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
1853 }
1854
1855 // (1) Record the value for the admin so it's sticky
1856 if (ap.encryptionRequested != encrypt) {
1857 ap.encryptionRequested = encrypt;
1858 saveSettingsLocked();
1859 }
1860
1861 // (2) Compute "max" for all admins
1862 boolean newRequested = false;
1863 final int N = mAdminList.size();
1864 for (int i = 0; i < N; i++) {
1865 newRequested |= mAdminList.get(i).encryptionRequested;
1866 }
1867
1868 // Notify OS of new request
1869 setEncryptionRequested(newRequested);
1870
1871 // Return the new global request status
1872 return newRequested
1873 ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
1874 : DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001875 }
1876 }
1877
1878 /**
Andy Stadler22dbfda2011-01-17 12:47:31 -08001879 * Get the current storage encryption request status for a given admin, or aggregate of all
1880 * active admins.
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001881 */
Andy Stadler22dbfda2011-01-17 12:47:31 -08001882 public boolean getStorageEncryption(ComponentName who) {
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001883 synchronized (this) {
1884 // Check for permissions if a particular caller is specified
1885 if (who != null) {
Andy Stadler22dbfda2011-01-17 12:47:31 -08001886 // When checking for a single caller, status is based on caller's request
1887 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1888 DeviceAdminInfo.USES_ENCRYPTED_STORAGE);
1889 return ap.encryptionRequested;
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001890 }
1891
Andy Stadler22dbfda2011-01-17 12:47:31 -08001892 // If no particular caller is specified, return the aggregate set of requests.
1893 // This is short circuited by returning true on the first hit.
1894 final int N = mAdminList.size();
1895 for (int i = 0; i < N; i++) {
1896 if (mAdminList.get(i).encryptionRequested) {
1897 return true;
1898 }
1899 }
1900 return false;
Andy Stadler7b0f8f02011-01-12 14:59:52 -08001901 }
1902 }
1903
Andy Stadler22dbfda2011-01-17 12:47:31 -08001904 /**
1905 * Get the current encryption status of the device.
1906 */
1907 public int getStorageEncryptionStatus() {
1908 return getEncryptionStatus();
1909 }
1910
1911 /**
1912 * Hook to low-levels: This should report if the filesystem supports encrypted storage.
1913 */
1914 private boolean isEncryptionSupported() {
1915 // Note, this can be implemented as
1916 // return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
1917 // But is provided as a separate internal method if there's a faster way to do a
1918 // simple check for supported-or-not.
1919 return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
1920 }
1921
1922 /**
1923 * Hook to low-levels: Reporting the current status of encryption.
1924 * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} or
1925 * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE} or
1926 * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
1927 */
1928 private int getEncryptionStatus() {
Andy Stadler0fe45de2011-01-20 16:35:09 -08001929 String status = SystemProperties.get("ro.crypto.state", "unsupported");
1930 if ("encrypted".equalsIgnoreCase(status)) {
1931 return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
1932 } else if ("unencrypted".equalsIgnoreCase(status)) {
1933 return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
1934 } else {
1935 return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
1936 }
Andy Stadler22dbfda2011-01-17 12:47:31 -08001937 }
1938
1939 /**
1940 * Hook to low-levels: If needed, record the new admin setting for encryption.
1941 */
1942 private void setEncryptionRequested(boolean encrypt) {
1943 }
1944
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001945 @Override
1946 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1947 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1948 != PackageManager.PERMISSION_GRANTED) {
1949
1950 pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid="
1951 + Binder.getCallingPid()
1952 + ", uid=" + Binder.getCallingUid());
1953 return;
1954 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001955
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001956 final Printer p = new PrintWriterPrinter(pw);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001957
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001958 synchronized (this) {
1959 p.println("Current Device Policy Manager state:");
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001960
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001961 p.println(" Enabled Device Admins:");
1962 final int N = mAdminList.size();
1963 for (int i=0; i<N; i++) {
1964 ActiveAdmin ap = mAdminList.get(i);
1965 if (ap != null) {
1966 pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString());
1967 pw.println(":");
1968 ap.dump(" ", pw);
1969 }
1970 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001971
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001972 pw.println(" ");
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001973 pw.print(" mActivePasswordQuality=0x");
1974 pw.println(Integer.toHexString(mActivePasswordQuality));
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001975 pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001976 pw.print(" mActivePasswordUpperCase="); pw.println(mActivePasswordUpperCase);
1977 pw.print(" mActivePasswordLowerCase="); pw.println(mActivePasswordLowerCase);
1978 pw.print(" mActivePasswordLetters="); pw.println(mActivePasswordLetters);
1979 pw.print(" mActivePasswordNumeric="); pw.println(mActivePasswordNumeric);
1980 pw.print(" mActivePasswordSymbols="); pw.println(mActivePasswordSymbols);
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001981 pw.print(" mActivePasswordNonLetter="); pw.println(mActivePasswordNonLetter);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001982 pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
1983 pw.print(" mPasswordOwner="); pw.println(mPasswordOwner);
1984 }
1985 }
Dianne Hackbornd6847842010-01-12 18:14:19 -08001986}