blob: 470af6720bb01980b7c129d79b859b2544a2682e [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;
Oscar Montemayor69238c62010-08-03 10:51:06 -070056import android.provider.Settings;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080057import android.util.PrintWriterPrinter;
58import android.util.Printer;
Andy Stadler1f35d482010-11-19 15:39:41 -080059import android.util.Slog;
Dianne Hackbornd6847842010-01-12 18:14:19 -080060import android.util.Xml;
Dianne Hackborn254cb442010-01-27 19:23:59 -080061import android.view.WindowManagerPolicy;
Dianne Hackbornd6847842010-01-12 18:14:19 -080062
63import java.io.File;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080064import java.io.FileDescriptor;
Dianne Hackbornd6847842010-01-12 18:14:19 -080065import java.io.FileInputStream;
Dianne Hackborncef65ee2010-09-30 18:27:22 -070066import java.io.FileNotFoundException;
Dianne Hackbornd6847842010-01-12 18:14:19 -080067import java.io.FileOutputStream;
68import java.io.IOException;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -080069import java.io.PrintWriter;
Jim Millera4e28d12010-11-08 16:15:47 -080070import java.text.DateFormat;
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -080071import java.util.ArrayList;
Jim Millera4e28d12010-11-08 16:15:47 -080072import java.util.Date;
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -080073import java.util.HashMap;
Dianne Hackbornd6847842010-01-12 18:14:19 -080074import java.util.List;
Oscar Montemayor69238c62010-08-03 10:51:06 -070075import java.util.Set;
Dianne Hackbornd6847842010-01-12 18:14:19 -080076
77/**
78 * Implementation of the device policy APIs.
79 */
80public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Jim Millera4e28d12010-11-08 16:15:47 -080081 private static final int REQUEST_EXPIRE_PASSWORD = 5571;
82
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080083 static final String TAG = "DevicePolicyManagerService";
Konstantin Lopyrev32558232010-05-20 16:18:05 -070084
Jim Millera4e28d12010-11-08 16:15:47 -080085 private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * 86400 * 1000; // 5 days, in ms
86
87 protected static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
88 = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
89
90 private static final long MS_PER_DAY = 86400 * 1000;
Jim Millera4e28d12010-11-08 16:15:47 -080091
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080092 final Context mContext;
93 final MyPackageMonitor mMonitor;
Dianne Hackborn42499172010-10-15 18:45:07 -070094 final PowerManager.WakeLock mWakeLock;
Dianne Hackbornd6847842010-01-12 18:14:19 -080095
Dianne Hackborndf83afa2010-01-20 13:37:26 -080096 IPowerManager mIPowerManager;
Konstantin Lopyrev32558232010-05-20 16:18:05 -070097
Dianne Hackborn9327f4f2010-01-29 10:38:29 -080098 int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackbornd6847842010-01-12 18:14:19 -080099 int mActivePasswordLength = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700100 int mActivePasswordUpperCase = 0;
101 int mActivePasswordLowerCase = 0;
102 int mActivePasswordLetters = 0;
103 int mActivePasswordNumeric = 0;
104 int mActivePasswordSymbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700105 int mActivePasswordNonLetter = 0;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800106 int mFailedPasswordAttempts = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700107
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800108 int mPasswordOwner = -1;
Jim Millera4e28d12010-11-08 16:15:47 -0800109 Handler mHandler = new Handler();
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700110
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800111 final HashMap<ComponentName, ActiveAdmin> mAdminMap
112 = new HashMap<ComponentName, ActiveAdmin>();
113 final ArrayList<ActiveAdmin> mAdminList
114 = new ArrayList<ActiveAdmin>();
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700115
Jim Millera4e28d12010-11-08 16:15:47 -0800116 BroadcastReceiver mReceiver = new BroadcastReceiver() {
117 @Override
118 public void onReceive(Context context, Intent intent) {
119 String action = intent.getAction();
120 if (Intent.ACTION_BOOT_COMPLETED.equals(action)
121 || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
122 Slog.v(TAG, "Sending password expiration notifications for action " + action);
123 mHandler.post(new Runnable() {
124 public void run() {
125 handlePasswordExpirationNotification();
126 }
127 });
128 }
129 }
130 };
131
Dianne Hackbornd6847842010-01-12 18:14:19 -0800132 static class ActiveAdmin {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800133 final DeviceAdminInfo info;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700134
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800135 int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800136 int minimumPasswordLength = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700137 int passwordHistoryLength = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700138 int minimumPasswordUpperCase = 0;
139 int minimumPasswordLowerCase = 0;
140 int minimumPasswordLetters = 1;
141 int minimumPasswordNumeric = 1;
142 int minimumPasswordSymbols = 1;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700143 int minimumPasswordNonLetter = 0;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800144 long maximumTimeToUnlock = 0;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800145 int maximumFailedPasswordsForWipe = 0;
Jim Millera4e28d12010-11-08 16:15:47 -0800146 long passwordExpirationTimeout = 0L;
147 long passwordExpirationDate = 0L;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700148
Oscar Montemayor69238c62010-08-03 10:51:06 -0700149 // TODO: review implementation decisions with frameworks team
150 boolean specifiesGlobalProxy = false;
151 String globalProxySpec = null;
152 String globalProxyExclusionList = null;
153
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800154 ActiveAdmin(DeviceAdminInfo _info) {
155 info = _info;
156 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700157
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800158 int getUid() { return info.getActivityInfo().applicationInfo.uid; }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700159
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800160 void writeToXml(XmlSerializer out)
161 throws IllegalArgumentException, IllegalStateException, IOException {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800162 out.startTag(null, "policies");
163 info.writePoliciesToXml(out);
164 out.endTag(null, "policies");
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800165 if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
166 out.startTag(null, "password-quality");
167 out.attribute(null, "value", Integer.toString(passwordQuality));
168 out.endTag(null, "password-quality");
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800169 if (minimumPasswordLength > 0) {
170 out.startTag(null, "min-password-length");
171 out.attribute(null, "value", Integer.toString(minimumPasswordLength));
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700172 out.endTag(null, "min-password-length");
173 }
174 if(passwordHistoryLength > 0) {
175 out.startTag(null, "password-history-length");
176 out.attribute(null, "value", Integer.toString(passwordHistoryLength));
177 out.endTag(null, "password-history-length");
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800178 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700179 if (minimumPasswordUpperCase > 0) {
180 out.startTag(null, "min-password-uppercase");
181 out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase));
182 out.endTag(null, "min-password-uppercase");
183 }
184 if (minimumPasswordLowerCase > 0) {
185 out.startTag(null, "min-password-lowercase");
186 out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase));
187 out.endTag(null, "min-password-lowercase");
188 }
189 if (minimumPasswordLetters > 0) {
190 out.startTag(null, "min-password-letters");
191 out.attribute(null, "value", Integer.toString(minimumPasswordLetters));
192 out.endTag(null, "min-password-letters");
193 }
194 if (minimumPasswordNumeric > 0) {
195 out.startTag(null, "min-password-numeric");
196 out.attribute(null, "value", Integer.toString(minimumPasswordNumeric));
197 out.endTag(null, "min-password-numeric");
198 }
199 if (minimumPasswordSymbols > 0) {
200 out.startTag(null, "min-password-symbols");
201 out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
202 out.endTag(null, "min-password-symbols");
203 }
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700204 if (minimumPasswordNonLetter > 0) {
205 out.startTag(null, "min-password-nonletter");
206 out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
207 out.endTag(null, "min-password-nonletter");
208 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800209 }
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700210 if (maximumTimeToUnlock != 0) {
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800211 out.startTag(null, "max-time-to-unlock");
212 out.attribute(null, "value", Long.toString(maximumTimeToUnlock));
213 out.endTag(null, "max-time-to-unlock");
214 }
215 if (maximumFailedPasswordsForWipe != 0) {
216 out.startTag(null, "max-failed-password-wipe");
217 out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe));
218 out.endTag(null, "max-failed-password-wipe");
219 }
Oscar Montemayor69238c62010-08-03 10:51:06 -0700220 if (specifiesGlobalProxy) {
221 out.startTag(null, "specifies-global-proxy");
222 out.attribute(null, "value", Boolean.toString(specifiesGlobalProxy));
223 out.endTag(null, "specifies_global_proxy");
224 if (globalProxySpec != null) {
225 out.startTag(null, "global-proxy-spec");
226 out.attribute(null, "value", globalProxySpec);
227 out.endTag(null, "global-proxy-spec");
228 }
229 if (globalProxyExclusionList != null) {
230 out.startTag(null, "global-proxy-exclusion-list");
231 out.attribute(null, "value", globalProxyExclusionList);
232 out.endTag(null, "global-proxy-exclusion-list");
233 }
234 }
Jim Millera4e28d12010-11-08 16:15:47 -0800235 if (passwordExpirationTimeout != 0L) {
236 out.startTag(null, "password-expiration-timeout");
237 out.attribute(null, "value", Long.toString(passwordExpirationTimeout));
238 out.endTag(null, "password-expiration-timeout");
239 }
240 if (passwordExpirationDate != 0L) {
241 out.startTag(null, "password-expiration-date");
242 out.attribute(null, "value", Long.toString(passwordExpirationDate));
243 out.endTag(null, "password-expiration-date");
244 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800245 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700246
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800247 void readFromXml(XmlPullParser parser)
248 throws XmlPullParserException, IOException {
249 int outerDepth = parser.getDepth();
250 int type;
251 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
252 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
253 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
254 continue;
255 }
256 String tag = parser.getName();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800257 if ("policies".equals(tag)) {
258 info.readPoliciesFromXml(parser);
259 } else if ("password-quality".equals(tag)) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800260 passwordQuality = Integer.parseInt(
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800261 parser.getAttributeValue(null, "value"));
262 } else if ("min-password-length".equals(tag)) {
263 minimumPasswordLength = Integer.parseInt(
264 parser.getAttributeValue(null, "value"));
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700265 } else if ("password-history-length".equals(tag)) {
266 passwordHistoryLength = Integer.parseInt(
267 parser.getAttributeValue(null, "value"));
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700268 } else if ("min-password-uppercase".equals(tag)) {
269 minimumPasswordUpperCase = Integer.parseInt(
270 parser.getAttributeValue(null, "value"));
271 } else if ("min-password-lowercase".equals(tag)) {
272 minimumPasswordLowerCase = Integer.parseInt(
273 parser.getAttributeValue(null, "value"));
274 } else if ("min-password-letters".equals(tag)) {
275 minimumPasswordLetters = Integer.parseInt(
276 parser.getAttributeValue(null, "value"));
277 } else if ("min-password-numeric".equals(tag)) {
278 minimumPasswordNumeric = Integer.parseInt(
279 parser.getAttributeValue(null, "value"));
280 } else if ("min-password-symbols".equals(tag)) {
281 minimumPasswordSymbols = Integer.parseInt(
282 parser.getAttributeValue(null, "value"));
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700283 } else if ("min-password-nonletter".equals(tag)) {
284 minimumPasswordNonLetter = Integer.parseInt(
285 parser.getAttributeValue(null, "value"));
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800286 } else if ("max-time-to-unlock".equals(tag)) {
287 maximumTimeToUnlock = Long.parseLong(
288 parser.getAttributeValue(null, "value"));
289 } else if ("max-failed-password-wipe".equals(tag)) {
290 maximumFailedPasswordsForWipe = Integer.parseInt(
291 parser.getAttributeValue(null, "value"));
Oscar Montemayor69238c62010-08-03 10:51:06 -0700292 } else if ("specifies-global-proxy".equals(tag)) {
293 specifiesGlobalProxy = Boolean.getBoolean(
294 parser.getAttributeValue(null, "value"));
295 } else if ("global-proxy-spec".equals(tag)) {
296 globalProxySpec =
297 parser.getAttributeValue(null, "value");
298 } else if ("global-proxy-exclusion-list".equals(tag)) {
299 globalProxyExclusionList =
300 parser.getAttributeValue(null, "value");
Jim Millera4e28d12010-11-08 16:15:47 -0800301 } else if ("password-expiration-timeout".equals(tag)) {
302 passwordExpirationTimeout = Long.parseLong(
303 parser.getAttributeValue(null, "value"));
304 } else if ("password-expiration-date".equals(tag)) {
305 passwordExpirationDate = Long.parseLong(
306 parser.getAttributeValue(null, "value"));
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800307 } else {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700308 Slog.w(TAG, "Unknown admin tag: " + tag);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800309 }
310 XmlUtils.skipCurrentTag(parser);
311 }
312 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700313
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800314 void dump(String prefix, PrintWriter pw) {
315 pw.print(prefix); pw.print("uid="); pw.println(getUid());
316 pw.print(prefix); pw.println("policies:");
317 ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
318 if (pols != null) {
319 for (int i=0; i<pols.size(); i++) {
320 pw.print(prefix); pw.print(" "); pw.println(pols.get(i).tag);
321 }
322 }
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700323 pw.print(prefix); pw.print("passwordQuality=0x");
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700324 pw.println(Integer.toHexString(passwordQuality));
325 pw.print(prefix); pw.print("minimumPasswordLength=");
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800326 pw.println(minimumPasswordLength);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700327 pw.print(prefix); pw.print("passwordHistoryLength=");
328 pw.println(passwordHistoryLength);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700329 pw.print(prefix); pw.print("minimumPasswordUpperCase=");
330 pw.println(minimumPasswordUpperCase);
331 pw.print(prefix); pw.print("minimumPasswordLowerCase=");
332 pw.println(minimumPasswordLowerCase);
333 pw.print(prefix); pw.print("minimumPasswordLetters=");
334 pw.println(minimumPasswordLetters);
335 pw.print(prefix); pw.print("minimumPasswordNumeric=");
336 pw.println(minimumPasswordNumeric);
337 pw.print(prefix); pw.print("minimumPasswordSymbols=");
338 pw.println(minimumPasswordSymbols);
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700339 pw.print(prefix); pw.print("minimumPasswordNonLetter=");
340 pw.println(minimumPasswordNonLetter);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800341 pw.print(prefix); pw.print("maximumTimeToUnlock=");
342 pw.println(maximumTimeToUnlock);
343 pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
344 pw.println(maximumFailedPasswordsForWipe);
Oscar Montemayor69238c62010-08-03 10:51:06 -0700345 pw.print(prefix); pw.print("specifiesGlobalProxy=");
346 pw.println(specifiesGlobalProxy);
Jim Millera4e28d12010-11-08 16:15:47 -0800347 pw.print(prefix); pw.print("passwordExpirationTimeout=");
348 pw.println(passwordExpirationTimeout);
349 pw.print(prefix); pw.print("passwordExpirationDate=");
350 pw.println(passwordExpirationDate);
Oscar Montemayor69238c62010-08-03 10:51:06 -0700351 if (globalProxySpec != null) {
352 pw.print(prefix); pw.print("globalProxySpec=");
353 pw.println(globalProxySpec);
354 }
355 if (globalProxyExclusionList != null) {
356 pw.print(prefix); pw.print("globalProxyEclusionList=");
357 pw.println(globalProxyExclusionList);
358 }
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800359 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800360 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700361
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800362 class MyPackageMonitor extends PackageMonitor {
Andy Stadler1f35d482010-11-19 15:39:41 -0800363 @Override
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800364 public void onSomePackagesChanged() {
365 synchronized (DevicePolicyManagerService.this) {
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800366 boolean removed = false;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800367 for (int i=mAdminList.size()-1; i>=0; i--) {
368 ActiveAdmin aa = mAdminList.get(i);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700369 int change = isPackageDisappearing(aa.info.getPackageName());
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800370 if (change == PACKAGE_PERMANENT_CHANGE
371 || change == PACKAGE_TEMPORARY_CHANGE) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700372 Slog.w(TAG, "Admin unexpectedly uninstalled: "
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800373 + aa.info.getComponent());
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800374 removed = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800375 mAdminList.remove(i);
376 } else if (isPackageModified(aa.info.getPackageName())) {
377 try {
378 mContext.getPackageManager().getReceiverInfo(
379 aa.info.getComponent(), 0);
380 } catch (NameNotFoundException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700381 Slog.w(TAG, "Admin package change removed component: "
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800382 + aa.info.getComponent());
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800383 removed = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800384 mAdminList.remove(i);
385 }
386 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800387 }
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800388 if (removed) {
389 validatePasswordOwnerLocked();
390 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800391 }
392 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800393 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700394
Dianne Hackbornd6847842010-01-12 18:14:19 -0800395 /**
396 * Instantiates the service.
397 */
398 public DevicePolicyManagerService(Context context) {
399 mContext = context;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800400 mMonitor = new MyPackageMonitor();
401 mMonitor.register(context, true);
Dianne Hackborn42499172010-10-15 18:45:07 -0700402 mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
403 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
Jim Millera4e28d12010-11-08 16:15:47 -0800404 IntentFilter filter = new IntentFilter();
405 filter.addAction(Intent.ACTION_BOOT_COMPLETED);
406 filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
407 context.registerReceiver(mReceiver, filter);
408 }
409
Andy Stadler043116a2010-11-29 17:43:32 -0800410 /**
411 * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
412 * reminders. Clears alarm if no expirations are configured.
413 */
Jim Millera4e28d12010-11-08 16:15:47 -0800414 protected void setExpirationAlarmCheckLocked(Context context) {
415 final long expiration = getPasswordExpirationLocked(null);
416 final long now = System.currentTimeMillis();
417 final long timeToExpire = expiration - now;
418 final long alarmTime;
Andy Stadler043116a2010-11-29 17:43:32 -0800419 if (expiration == 0) {
420 // No expirations are currently configured: Cancel alarm.
421 alarmTime = 0;
422 } else if (timeToExpire <= 0) {
423 // The password has already expired: Repeat every 24 hours.
Jim Millera4e28d12010-11-08 16:15:47 -0800424 alarmTime = now + MS_PER_DAY;
Andy Stadler043116a2010-11-29 17:43:32 -0800425 } else {
426 // Selecting the next alarm time: Roll forward to the next 24 hour multiple before
427 // the expiration time.
428 long alarmInterval = timeToExpire % MS_PER_DAY;
429 if (alarmInterval == 0) {
430 alarmInterval = MS_PER_DAY;
431 }
432 alarmTime = now + alarmInterval;
Jim Millera4e28d12010-11-08 16:15:47 -0800433 }
434
Andy Stadler1f35d482010-11-19 15:39:41 -0800435 long token = Binder.clearCallingIdentity();
436 try {
437 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
438 PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_EXPIRE_PASSWORD,
439 new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
440 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
441 am.cancel(pi);
Andy Stadler043116a2010-11-29 17:43:32 -0800442 if (alarmTime != 0) {
443 am.set(AlarmManager.RTC, alarmTime, pi);
444 }
Andy Stadler1f35d482010-11-19 15:39:41 -0800445 } finally {
446 Binder.restoreCallingIdentity(token);
447 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800448 }
449
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800450 private IPowerManager getIPowerManager() {
451 if (mIPowerManager == null) {
452 IBinder b = ServiceManager.getService(Context.POWER_SERVICE);
453 mIPowerManager = IPowerManager.Stub.asInterface(b);
454 }
455 return mIPowerManager;
456 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700457
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800458 ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800459 ActiveAdmin admin = mAdminMap.get(who);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800460 if (admin != null
461 && who.getPackageName().equals(admin.info.getActivityInfo().packageName)
462 && who.getClassName().equals(admin.info.getActivityInfo().name)) {
463 return admin;
464 }
465 return null;
466 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700467
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800468 ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
469 throws SecurityException {
Dianne Hackborn254cb442010-01-27 19:23:59 -0800470 final int callingUid = Binder.getCallingUid();
471 if (who != null) {
472 ActiveAdmin admin = mAdminMap.get(who);
473 if (admin == null) {
474 throw new SecurityException("No active admin " + who);
475 }
476 if (admin.getUid() != callingUid) {
477 throw new SecurityException("Admin " + who + " is not owned by uid "
478 + Binder.getCallingUid());
479 }
480 if (!admin.info.usesPolicy(reqPolicy)) {
481 throw new SecurityException("Admin " + admin.info.getComponent()
482 + " did not specify uses-policy for: "
483 + admin.info.getTagForPolicy(reqPolicy));
484 }
485 return admin;
486 } else {
487 final int N = mAdminList.size();
488 for (int i=0; i<N; i++) {
489 ActiveAdmin admin = mAdminList.get(i);
490 if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) {
491 return admin;
492 }
493 }
494 throw new SecurityException("No active admin owned by uid "
495 + Binder.getCallingUid() + " for policy #" + reqPolicy);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800496 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800497 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700498
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800499 void sendAdminCommandLocked(ActiveAdmin admin, String action) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800500 Intent intent = new Intent(action);
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800501 intent.setComponent(admin.info.getComponent());
Jim Millera4e28d12010-11-08 16:15:47 -0800502 if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
503 intent.putExtra("expiration", admin.passwordExpirationDate);
504 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800505 mContext.sendBroadcast(intent);
506 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700507
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800508 void sendAdminCommandLocked(String action, int reqPolicy) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800509 final int N = mAdminList.size();
510 if (N > 0) {
511 for (int i=0; i<N; i++) {
512 ActiveAdmin admin = mAdminList.get(i);
513 if (admin.info.usesPolicy(reqPolicy)) {
514 sendAdminCommandLocked(admin, action);
515 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800516 }
Dianne Hackborn4141d032010-01-21 16:29:00 -0800517 }
518 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700519
Dianne Hackbornd6847842010-01-12 18:14:19 -0800520 void removeActiveAdminLocked(ComponentName adminReceiver) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800521 ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
522 if (admin != null) {
Oscar Montemayor69238c62010-08-03 10:51:06 -0700523 boolean doProxyCleanup =
524 admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800525 sendAdminCommandLocked(admin,
Dianne Hackbornef6b22f2010-02-16 20:38:49 -0800526 DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800527 // XXX need to wait for it to complete.
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800528 mAdminList.remove(admin);
529 mAdminMap.remove(adminReceiver);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800530 validatePasswordOwnerLocked();
Oscar Montemayor69238c62010-08-03 10:51:06 -0700531 if (doProxyCleanup) {
532 resetGlobalProxy();
533 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800534 }
535 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700536
Dianne Hackbornd6847842010-01-12 18:14:19 -0800537 public DeviceAdminInfo findAdmin(ComponentName adminName) {
538 Intent resolveIntent = new Intent();
539 resolveIntent.setComponent(adminName);
540 List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
541 resolveIntent, PackageManager.GET_META_DATA);
542 if (infos == null || infos.size() <= 0) {
543 throw new IllegalArgumentException("Unknown admin: " + adminName);
544 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700545
Dianne Hackbornd6847842010-01-12 18:14:19 -0800546 try {
547 return new DeviceAdminInfo(mContext, infos.get(0));
548 } catch (XmlPullParserException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700549 Slog.w(TAG, "Bad device admin requested: " + adminName, e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800550 return null;
551 } catch (IOException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700552 Slog.w(TAG, "Bad device admin requested: " + adminName, e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800553 return null;
554 }
555 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700556
Dianne Hackbornd6847842010-01-12 18:14:19 -0800557 private static JournaledFile makeJournaledFile() {
558 final String base = "/data/system/device_policies.xml";
559 return new JournaledFile(new File(base), new File(base + ".tmp"));
560 }
561
562 private void saveSettingsLocked() {
563 JournaledFile journal = makeJournaledFile();
564 FileOutputStream stream = null;
565 try {
566 stream = new FileOutputStream(journal.chooseForWrite(), false);
567 XmlSerializer out = new FastXmlSerializer();
568 out.setOutput(stream, "utf-8");
569 out.startDocument(null, true);
570
571 out.startTag(null, "policies");
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700572
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800573 final int N = mAdminList.size();
574 for (int i=0; i<N; i++) {
575 ActiveAdmin ap = mAdminList.get(i);
576 if (ap != null) {
577 out.startTag(null, "admin");
578 out.attribute(null, "name", ap.info.getComponent().flattenToString());
579 ap.writeToXml(out);
580 out.endTag(null, "admin");
581 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800582 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700583
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800584 if (mPasswordOwner >= 0) {
585 out.startTag(null, "password-owner");
586 out.attribute(null, "value", Integer.toString(mPasswordOwner));
587 out.endTag(null, "password-owner");
588 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700589
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800590 if (mFailedPasswordAttempts != 0) {
591 out.startTag(null, "failed-password-attempts");
592 out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts));
593 out.endTag(null, "failed-password-attempts");
594 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700595
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700596 if (mActivePasswordQuality != 0 || mActivePasswordLength != 0
597 || mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0
598 || mActivePasswordLetters != 0 || mActivePasswordNumeric != 0
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700599 || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700600 out.startTag(null, "active-password");
601 out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
602 out.attribute(null, "length", Integer.toString(mActivePasswordLength));
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700603 out.attribute(null, "uppercase", Integer.toString(mActivePasswordUpperCase));
604 out.attribute(null, "lowercase", Integer.toString(mActivePasswordLowerCase));
605 out.attribute(null, "letters", Integer.toString(mActivePasswordLetters));
606 out.attribute(null, "numeric", Integer
607 .toString(mActivePasswordNumeric));
608 out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols));
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700609 out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter));
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700610 out.endTag(null, "active-password");
611 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700612
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700613 out.endTag(null, "policies");
614
Dianne Hackbornd6847842010-01-12 18:14:19 -0800615 out.endDocument();
616 stream.close();
617 journal.commit();
Jim Miller284b62e2010-06-08 14:27:42 -0700618 sendChangedNotification();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800619 } catch (IOException e) {
620 try {
621 if (stream != null) {
622 stream.close();
623 }
624 } catch (IOException ex) {
625 // Ignore
626 }
627 journal.rollback();
628 }
629 }
630
Jim Miller284b62e2010-06-08 14:27:42 -0700631 private void sendChangedNotification() {
632 Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
633 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
634 mContext.sendBroadcast(intent);
635 }
636
Dianne Hackbornd6847842010-01-12 18:14:19 -0800637 private void loadSettingsLocked() {
638 JournaledFile journal = makeJournaledFile();
639 FileInputStream stream = null;
640 File file = journal.chooseForRead();
Dianne Hackbornd6847842010-01-12 18:14:19 -0800641 try {
642 stream = new FileInputStream(file);
643 XmlPullParser parser = Xml.newPullParser();
644 parser.setInput(stream, null);
645
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800646 int type;
647 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
648 && type != XmlPullParser.START_TAG) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800649 }
650 String tag = parser.getName();
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800651 if (!"policies".equals(tag)) {
652 throw new XmlPullParserException(
653 "Settings do not start with policies tag: found " + tag);
654 }
655 type = parser.next();
656 int outerDepth = parser.getDepth();
657 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
658 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
659 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
660 continue;
661 }
662 tag = parser.getName();
663 if ("admin".equals(tag)) {
Dianne Hackborne83cefce2010-02-04 17:38:14 -0800664 String name = parser.getAttributeValue(null, "name");
665 try {
666 DeviceAdminInfo dai = findAdmin(
667 ComponentName.unflattenFromString(name));
668 if (dai != null) {
669 ActiveAdmin ap = new ActiveAdmin(dai);
670 ap.readFromXml(parser);
671 mAdminMap.put(ap.info.getComponent(), ap);
672 mAdminList.add(ap);
673 }
674 } catch (RuntimeException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700675 Slog.w(TAG, "Failed loading admin " + name, e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800676 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800677 } else if ("failed-password-attempts".equals(tag)) {
678 mFailedPasswordAttempts = Integer.parseInt(
679 parser.getAttributeValue(null, "value"));
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800680 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800681 } else if ("password-owner".equals(tag)) {
682 mPasswordOwner = Integer.parseInt(
683 parser.getAttributeValue(null, "value"));
684 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700685 } else if ("active-password".equals(tag)) {
686 mActivePasswordQuality = Integer.parseInt(
687 parser.getAttributeValue(null, "quality"));
688 mActivePasswordLength = Integer.parseInt(
689 parser.getAttributeValue(null, "length"));
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700690 mActivePasswordUpperCase = Integer.parseInt(
691 parser.getAttributeValue(null, "uppercase"));
692 mActivePasswordLowerCase = Integer.parseInt(
693 parser.getAttributeValue(null, "lowercase"));
694 mActivePasswordLetters = Integer.parseInt(
695 parser.getAttributeValue(null, "letters"));
696 mActivePasswordNumeric = Integer.parseInt(
697 parser.getAttributeValue(null, "numeric"));
698 mActivePasswordSymbols = Integer.parseInt(
699 parser.getAttributeValue(null, "symbols"));
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700700 mActivePasswordNonLetter = Integer.parseInt(
701 parser.getAttributeValue(null, "nonletter"));
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700702 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800703 } else {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700704 Slog.w(TAG, "Unknown tag: " + tag);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800705 XmlUtils.skipCurrentTag(parser);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -0800706 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800707 }
708 } catch (NullPointerException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700709 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800710 } catch (NumberFormatException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700711 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800712 } catch (XmlPullParserException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700713 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackborncef65ee2010-09-30 18:27:22 -0700714 } catch (FileNotFoundException e) {
715 // Don't be noisy, this is normal if we haven't defined any policies.
Dianne Hackbornd6847842010-01-12 18:14:19 -0800716 } catch (IOException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700717 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800718 } catch (IndexOutOfBoundsException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700719 Slog.w(TAG, "failed parsing " + file + " " + e);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800720 }
721 try {
722 if (stream != null) {
723 stream.close();
724 }
725 } catch (IOException e) {
726 // Ignore
727 }
728
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700729 // Validate that what we stored for the password quality matches
730 // sufficiently what is currently set. Note that this is only
731 // a sanity check in case the two get out of sync; this should
732 // never normally happen.
733 LockPatternUtils utils = new LockPatternUtils(mContext);
734 if (utils.getActivePasswordQuality() < mActivePasswordQuality) {
735 Slog.w(TAG, "Active password quality 0x"
736 + Integer.toHexString(mActivePasswordQuality)
737 + " does not match actual quality 0x"
738 + Integer.toHexString(utils.getActivePasswordQuality()));
739 mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
740 mActivePasswordLength = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700741 mActivePasswordUpperCase = 0;
742 mActivePasswordLowerCase = 0;
743 mActivePasswordLetters = 0;
744 mActivePasswordNumeric = 0;
745 mActivePasswordSymbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -0700746 mActivePasswordNonLetter = 0;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700747 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700748
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800749 validatePasswordOwnerLocked();
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700750
Dianne Hackborn254cb442010-01-27 19:23:59 -0800751 long timeMs = getMaximumTimeToLock(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800752 if (timeMs <= 0) {
753 timeMs = Integer.MAX_VALUE;
754 }
755 try {
756 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
757 } catch (RemoteException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700758 Slog.w(TAG, "Failure talking with power manager", e);
Dianne Hackborndf83afa2010-01-20 13:37:26 -0800759 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800760 }
761
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700762 static void validateQualityConstant(int quality) {
763 switch (quality) {
764 case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
765 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
766 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
767 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
768 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -0700769 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700770 return;
771 }
772 throw new IllegalArgumentException("Invalid quality constant: 0x"
773 + Integer.toHexString(quality));
774 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700775
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800776 void validatePasswordOwnerLocked() {
777 if (mPasswordOwner >= 0) {
778 boolean haveOwner = false;
779 for (int i=mAdminList.size()-1; i>=0; i--) {
780 if (mAdminList.get(i).getUid() == mPasswordOwner) {
781 haveOwner = true;
782 break;
783 }
784 }
785 if (!haveOwner) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700786 Slog.w(TAG, "Previous password owner " + mPasswordOwner
Dianne Hackborn87bba1e2010-02-26 17:25:54 -0800787 + " no longer active; disabling");
788 mPasswordOwner = -1;
789 }
790 }
791 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700792
Dianne Hackbornd6847842010-01-12 18:14:19 -0800793 public void systemReady() {
794 synchronized (this) {
795 loadSettingsLocked();
796 }
797 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700798
Jim Millera4e28d12010-11-08 16:15:47 -0800799 private void handlePasswordExpirationNotification() {
800 synchronized (this) {
801 final long now = System.currentTimeMillis();
802 final int N = mAdminList.size();
803 if (N <= 0) {
804 return;
805 }
806 for (int i=0; i < N; i++) {
807 ActiveAdmin admin = mAdminList.get(i);
808 if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
809 && admin.passwordExpirationTimeout > 0L
810 && admin.passwordExpirationDate > 0L
Andy Stadler043116a2010-11-29 17:43:32 -0800811 && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
Jim Millera4e28d12010-11-08 16:15:47 -0800812 sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
813 }
814 }
815 setExpirationAlarmCheckLocked(mContext);
816 }
817 }
818
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800819 /**
820 * @param adminReceiver The admin to add
821 * @param refreshing true = update an active admin, no error
822 */
823 public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800824 mContext.enforceCallingOrSelfPermission(
825 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700826
Dianne Hackbornd6847842010-01-12 18:14:19 -0800827 DeviceAdminInfo info = findAdmin(adminReceiver);
828 if (info == null) {
829 throw new IllegalArgumentException("Bad admin: " + adminReceiver);
830 }
831 synchronized (this) {
832 long ident = Binder.clearCallingIdentity();
833 try {
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800834 if (!refreshing && getActiveAdminUncheckedLocked(adminReceiver) != null) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800835 throw new IllegalArgumentException("Admin is already added");
Dianne Hackbornd6847842010-01-12 18:14:19 -0800836 }
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800837 ActiveAdmin newAdmin = new ActiveAdmin(info);
838 mAdminMap.put(adminReceiver, newAdmin);
839 int replaceIndex = -1;
840 if (refreshing) {
841 final int N = mAdminList.size();
842 for (int i=0; i < N; i++) {
843 ActiveAdmin oldAdmin = mAdminList.get(i);
844 if (oldAdmin.info.getComponent().equals(adminReceiver)) {
845 replaceIndex = i;
846 break;
847 }
848 }
849 }
850 if (replaceIndex == -1) {
851 mAdminList.add(newAdmin);
852 } else {
853 mAdminList.set(replaceIndex, newAdmin);
854 }
Dianne Hackbornd6847842010-01-12 18:14:19 -0800855 saveSettingsLocked();
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800856 sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800857 } finally {
858 Binder.restoreCallingIdentity(ident);
859 }
860 }
861 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700862
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800863 public boolean isAdminActive(ComponentName adminReceiver) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800864 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800865 return getActiveAdminUncheckedLocked(adminReceiver) != null;
866 }
867 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700868
Andy Stadlerc25f70a2010-12-08 15:56:45 -0800869 public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId) {
870 synchronized (this) {
871 ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver);
872 if (administrator == null) {
873 throw new SecurityException("No active admin " + adminReceiver);
874 }
875 return administrator.info.usesPolicy(policyId);
876 }
877 }
878
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800879 public List<ComponentName> getActiveAdmins() {
880 synchronized (this) {
881 final int N = mAdminList.size();
882 if (N <= 0) {
883 return null;
884 }
885 ArrayList<ComponentName> res = new ArrayList<ComponentName>(N);
886 for (int i=0; i<N; i++) {
887 res.add(mAdminList.get(i).info.getComponent());
888 }
889 return res;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800890 }
891 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700892
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800893 public boolean packageHasActiveAdmins(String packageName) {
894 synchronized (this) {
895 final int N = mAdminList.size();
896 for (int i=0; i<N; i++) {
897 if (mAdminList.get(i).info.getPackageName().equals(packageName)) {
898 return true;
899 }
900 }
901 return false;
902 }
903 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700904
Dianne Hackbornd6847842010-01-12 18:14:19 -0800905 public void removeActiveAdmin(ComponentName adminReceiver) {
906 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800907 ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
908 if (admin == null) {
909 return;
910 }
911 if (admin.getUid() != Binder.getCallingUid()) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800912 mContext.enforceCallingOrSelfPermission(
913 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
914 }
915 long ident = Binder.clearCallingIdentity();
916 try {
917 removeActiveAdminLocked(adminReceiver);
918 } finally {
919 Binder.restoreCallingIdentity(ident);
920 }
921 }
922 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700923
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700924 public void setPasswordQuality(ComponentName who, int quality) {
925 validateQualityConstant(quality);
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700926
Dianne Hackbornd6847842010-01-12 18:14:19 -0800927 synchronized (this) {
928 if (who == null) {
929 throw new NullPointerException("ComponentName is null");
930 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800931 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
932 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -0700933 if (ap.passwordQuality != quality) {
934 ap.passwordQuality = quality;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800935 saveSettingsLocked();
936 }
937 }
938 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700939
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800940 public int getPasswordQuality(ComponentName who) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800941 synchronized (this) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800942 int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700943
Dianne Hackborn254cb442010-01-27 19:23:59 -0800944 if (who != null) {
945 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800946 return admin != null ? admin.passwordQuality : mode;
Dianne Hackborn254cb442010-01-27 19:23:59 -0800947 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700948
Dianne Hackborn254cb442010-01-27 19:23:59 -0800949 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800950 for (int i=0; i<N; i++) {
951 ActiveAdmin admin = mAdminList.get(i);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -0800952 if (mode < admin.passwordQuality) {
953 mode = admin.passwordQuality;
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800954 }
955 }
956 return mode;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800957 }
958 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700959
Dianne Hackborn254cb442010-01-27 19:23:59 -0800960 public void setPasswordMinimumLength(ComponentName who, int length) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800961 synchronized (this) {
962 if (who == null) {
963 throw new NullPointerException("ComponentName is null");
964 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -0800965 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
966 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -0800967 if (ap.minimumPasswordLength != length) {
968 ap.minimumPasswordLength = length;
969 saveSettingsLocked();
970 }
971 }
972 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700973
Dianne Hackborn254cb442010-01-27 19:23:59 -0800974 public int getPasswordMinimumLength(ComponentName who) {
Dianne Hackbornd6847842010-01-12 18:14:19 -0800975 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800976 int length = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700977
Dianne Hackborn254cb442010-01-27 19:23:59 -0800978 if (who != null) {
979 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
980 return admin != null ? admin.minimumPasswordLength : length;
981 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700982
Dianne Hackborn254cb442010-01-27 19:23:59 -0800983 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -0800984 for (int i=0; i<N; i++) {
985 ActiveAdmin admin = mAdminList.get(i);
986 if (length < admin.minimumPasswordLength) {
987 length = admin.minimumPasswordLength;
988 }
989 }
990 return length;
Dianne Hackbornd6847842010-01-12 18:14:19 -0800991 }
992 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -0700993
994 public void setPasswordHistoryLength(ComponentName who, int length) {
995 synchronized (this) {
996 if (who == null) {
997 throw new NullPointerException("ComponentName is null");
998 }
999 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1000 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1001 if (ap.passwordHistoryLength != length) {
1002 ap.passwordHistoryLength = length;
1003 saveSettingsLocked();
1004 }
1005 }
1006 }
1007
1008 public int getPasswordHistoryLength(ComponentName who) {
1009 synchronized (this) {
1010 int length = 0;
1011
1012 if (who != null) {
1013 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1014 return admin != null ? admin.passwordHistoryLength : length;
1015 }
1016
1017 final int N = mAdminList.size();
1018 for (int i = 0; i < N; i++) {
1019 ActiveAdmin admin = mAdminList.get(i);
1020 if (length < admin.passwordHistoryLength) {
1021 length = admin.passwordHistoryLength;
1022 }
1023 }
1024 return length;
1025 }
1026 }
1027
Jim Millera4e28d12010-11-08 16:15:47 -08001028 public void setPasswordExpirationTimeout(ComponentName who, long timeout) {
1029 synchronized (this) {
1030 if (who == null) {
1031 throw new NullPointerException("ComponentName is null");
1032 }
Andy Stadler1f35d482010-11-19 15:39:41 -08001033 if (timeout < 0) {
1034 throw new IllegalArgumentException("Timeout must be >= 0 ms");
Jim Millera4e28d12010-11-08 16:15:47 -08001035 }
1036 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1037 DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
1038 // Calling this API automatically bumps the expiration date
1039 final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
1040 ap.passwordExpirationDate = expiration;
1041 ap.passwordExpirationTimeout = timeout;
1042 if (timeout > 0L) {
1043 Slog.w(TAG, "setPasswordExpiration(): password will expire on "
1044 + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)
1045 .format(new Date(expiration)));
1046 }
1047 saveSettingsLocked();
1048 setExpirationAlarmCheckLocked(mContext); // in case this is the first one
1049 }
1050 }
1051
Andy Stadler043116a2010-11-29 17:43:32 -08001052 /**
1053 * Return a single admin's expiration cycle time, or the min of all cycle times.
1054 * Returns 0 if not configured.
1055 */
Jim Millera4e28d12010-11-08 16:15:47 -08001056 public long getPasswordExpirationTimeout(ComponentName who) {
1057 synchronized (this) {
Jim Millera4e28d12010-11-08 16:15:47 -08001058 if (who != null) {
1059 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
Andy Stadler043116a2010-11-29 17:43:32 -08001060 return admin != null ? admin.passwordExpirationTimeout : 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001061 }
1062
Andy Stadler043116a2010-11-29 17:43:32 -08001063 long timeout = 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001064 final int N = mAdminList.size();
1065 for (int i = 0; i < N; i++) {
1066 ActiveAdmin admin = mAdminList.get(i);
1067 if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
1068 && timeout > admin.passwordExpirationTimeout)) {
1069 timeout = admin.passwordExpirationTimeout;
1070 }
1071 }
1072 return timeout;
1073 }
1074 }
1075
Andy Stadler043116a2010-11-29 17:43:32 -08001076 /**
1077 * Return a single admin's expiration date/time, or the min (soonest) for all admins.
1078 * Returns 0 if not configured.
1079 */
Jim Millera4e28d12010-11-08 16:15:47 -08001080 private long getPasswordExpirationLocked(ComponentName who) {
Jim Millera4e28d12010-11-08 16:15:47 -08001081 if (who != null) {
1082 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
Andy Stadler043116a2010-11-29 17:43:32 -08001083 return admin != null ? admin.passwordExpirationDate : 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001084 }
1085
Andy Stadler043116a2010-11-29 17:43:32 -08001086 long timeout = 0L;
Jim Millera4e28d12010-11-08 16:15:47 -08001087 final int N = mAdminList.size();
1088 for (int i = 0; i < N; i++) {
1089 ActiveAdmin admin = mAdminList.get(i);
1090 if (timeout == 0L || (admin.passwordExpirationDate != 0
1091 && timeout > admin.passwordExpirationDate)) {
1092 timeout = admin.passwordExpirationDate;
1093 }
1094 }
1095 return timeout;
1096 }
1097
1098 public long getPasswordExpiration(ComponentName who) {
1099 synchronized (this) {
1100 return getPasswordExpirationLocked(who);
1101 }
1102 }
1103
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001104 public void setPasswordMinimumUpperCase(ComponentName who, int length) {
1105 synchronized (this) {
1106 if (who == null) {
1107 throw new NullPointerException("ComponentName is null");
1108 }
1109 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1110 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1111 if (ap.minimumPasswordUpperCase != length) {
1112 ap.minimumPasswordUpperCase = length;
1113 saveSettingsLocked();
1114 }
1115 }
1116 }
1117
1118 public int getPasswordMinimumUpperCase(ComponentName who) {
1119 synchronized (this) {
1120 int length = 0;
1121
1122 if (who != null) {
1123 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1124 return admin != null ? admin.minimumPasswordUpperCase : length;
1125 }
1126
1127 final int N = mAdminList.size();
1128 for (int i=0; i<N; i++) {
1129 ActiveAdmin admin = mAdminList.get(i);
1130 if (length < admin.minimumPasswordUpperCase) {
1131 length = admin.minimumPasswordUpperCase;
1132 }
1133 }
1134 return length;
1135 }
1136 }
1137
1138 public void setPasswordMinimumLowerCase(ComponentName who, int length) {
1139 synchronized (this) {
1140 if (who == null) {
1141 throw new NullPointerException("ComponentName is null");
1142 }
1143 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1144 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1145 if (ap.minimumPasswordLowerCase != length) {
1146 ap.minimumPasswordLowerCase = length;
1147 saveSettingsLocked();
1148 }
1149 }
1150 }
1151
1152 public int getPasswordMinimumLowerCase(ComponentName who) {
1153 synchronized (this) {
1154 int length = 0;
1155
1156 if (who != null) {
1157 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1158 return admin != null ? admin.minimumPasswordLowerCase : length;
1159 }
1160
1161 final int N = mAdminList.size();
1162 for (int i=0; i<N; i++) {
1163 ActiveAdmin admin = mAdminList.get(i);
1164 if (length < admin.minimumPasswordLowerCase) {
1165 length = admin.minimumPasswordLowerCase;
1166 }
1167 }
1168 return length;
1169 }
1170 }
1171
1172 public void setPasswordMinimumLetters(ComponentName who, int length) {
1173 synchronized (this) {
1174 if (who == null) {
1175 throw new NullPointerException("ComponentName is null");
1176 }
1177 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1178 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1179 if (ap.minimumPasswordLetters != length) {
1180 ap.minimumPasswordLetters = length;
1181 saveSettingsLocked();
1182 }
1183 }
1184 }
1185
1186 public int getPasswordMinimumLetters(ComponentName who) {
1187 synchronized (this) {
1188 int length = 0;
1189
1190 if (who != null) {
1191 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1192 return admin != null ? admin.minimumPasswordLetters : length;
1193 }
1194
1195 final int N = mAdminList.size();
1196 for (int i=0; i<N; i++) {
1197 ActiveAdmin admin = mAdminList.get(i);
1198 if (length < admin.minimumPasswordLetters) {
1199 length = admin.minimumPasswordLetters;
1200 }
1201 }
1202 return length;
1203 }
1204 }
1205
1206 public void setPasswordMinimumNumeric(ComponentName who, int length) {
1207 synchronized (this) {
1208 if (who == null) {
1209 throw new NullPointerException("ComponentName is null");
1210 }
1211 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1212 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1213 if (ap.minimumPasswordNumeric != length) {
1214 ap.minimumPasswordNumeric = length;
1215 saveSettingsLocked();
1216 }
1217 }
1218 }
1219
1220 public int getPasswordMinimumNumeric(ComponentName who) {
1221 synchronized (this) {
1222 int length = 0;
1223
1224 if (who != null) {
1225 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1226 return admin != null ? admin.minimumPasswordNumeric : length;
1227 }
1228
1229 final int N = mAdminList.size();
1230 for (int i = 0; i < N; i++) {
1231 ActiveAdmin admin = mAdminList.get(i);
1232 if (length < admin.minimumPasswordNumeric) {
1233 length = admin.minimumPasswordNumeric;
1234 }
1235 }
1236 return length;
1237 }
1238 }
1239
1240 public void setPasswordMinimumSymbols(ComponentName who, int length) {
1241 synchronized (this) {
1242 if (who == null) {
1243 throw new NullPointerException("ComponentName is null");
1244 }
1245 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1246 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1247 if (ap.minimumPasswordSymbols != length) {
1248 ap.minimumPasswordSymbols = length;
1249 saveSettingsLocked();
1250 }
1251 }
1252 }
1253
1254 public int getPasswordMinimumSymbols(ComponentName who) {
1255 synchronized (this) {
1256 int length = 0;
1257
1258 if (who != null) {
1259 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1260 return admin != null ? admin.minimumPasswordSymbols : length;
1261 }
1262
1263 final int N = mAdminList.size();
1264 for (int i=0; i<N; i++) {
1265 ActiveAdmin admin = mAdminList.get(i);
1266 if (length < admin.minimumPasswordSymbols) {
1267 length = admin.minimumPasswordSymbols;
1268 }
1269 }
1270 return length;
1271 }
1272 }
1273
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001274 public void setPasswordMinimumNonLetter(ComponentName who, int length) {
1275 synchronized (this) {
1276 if (who == null) {
1277 throw new NullPointerException("ComponentName is null");
1278 }
1279 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1280 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
1281 if (ap.minimumPasswordNonLetter != length) {
1282 ap.minimumPasswordNonLetter = length;
1283 saveSettingsLocked();
1284 }
1285 }
1286 }
1287
1288 public int getPasswordMinimumNonLetter(ComponentName who) {
1289 synchronized (this) {
1290 int length = 0;
1291
1292 if (who != null) {
1293 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1294 return admin != null ? admin.minimumPasswordNonLetter : length;
1295 }
1296
1297 final int N = mAdminList.size();
1298 for (int i=0; i<N; i++) {
1299 ActiveAdmin admin = mAdminList.get(i);
1300 if (length < admin.minimumPasswordNonLetter) {
1301 length = admin.minimumPasswordNonLetter;
1302 }
1303 }
1304 return length;
1305 }
1306 }
1307
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001308 public boolean isActivePasswordSufficient() {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001309 synchronized (this) {
1310 // This API can only be called by an active device admin,
1311 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001312 getActiveAdminForCallerLocked(null,
1313 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001314 if (mActivePasswordQuality < getPasswordQuality(null)
1315 || mActivePasswordLength < getPasswordMinimumLength(null)) {
1316 return false;
1317 }
1318 if(mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
1319 return true;
1320 }
1321 return mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null)
1322 && mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null)
1323 && mActivePasswordLetters >= getPasswordMinimumLetters(null)
1324 && mActivePasswordNumeric >= getPasswordMinimumNumeric(null)
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001325 && mActivePasswordSymbols >= getPasswordMinimumSymbols(null)
1326 && mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001327 }
1328 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001329
Dianne Hackbornd6847842010-01-12 18:14:19 -08001330 public int getCurrentFailedPasswordAttempts() {
1331 synchronized (this) {
1332 // This API can only be called by an active device admin,
1333 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001334 getActiveAdminForCallerLocked(null,
1335 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001336 return mFailedPasswordAttempts;
1337 }
1338 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001339
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001340 public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
1341 synchronized (this) {
1342 // This API can only be called by an active device admin,
1343 // so try to retrieve it to check that the caller is one.
1344 getActiveAdminForCallerLocked(who,
1345 DeviceAdminInfo.USES_POLICY_WIPE_DATA);
1346 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
1347 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
1348 if (ap.maximumFailedPasswordsForWipe != num) {
1349 ap.maximumFailedPasswordsForWipe = num;
1350 saveSettingsLocked();
1351 }
1352 }
1353 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001354
Dianne Hackborn254cb442010-01-27 19:23:59 -08001355 public int getMaximumFailedPasswordsForWipe(ComponentName who) {
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001356 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001357 int count = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001358
Dianne Hackborn254cb442010-01-27 19:23:59 -08001359 if (who != null) {
1360 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1361 return admin != null ? admin.maximumFailedPasswordsForWipe : count;
1362 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001363
Dianne Hackborn254cb442010-01-27 19:23:59 -08001364 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001365 for (int i=0; i<N; i++) {
1366 ActiveAdmin admin = mAdminList.get(i);
1367 if (count == 0) {
1368 count = admin.maximumFailedPasswordsForWipe;
1369 } else if (admin.maximumFailedPasswordsForWipe != 0
1370 && count > admin.maximumFailedPasswordsForWipe) {
1371 count = admin.maximumFailedPasswordsForWipe;
1372 }
1373 }
1374 return count;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001375 }
1376 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001377
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001378 public boolean resetPassword(String password, int flags) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001379 int quality;
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001380 synchronized (this) {
1381 // This API can only be called by an active device admin,
1382 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001383 getActiveAdminForCallerLocked(null,
1384 DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001385 quality = getPasswordQuality(null);
1386 if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001387 int realQuality = LockPatternUtils.computePasswordQuality(password);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001388 if (realQuality < quality
1389 && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001390 Slog.w(TAG, "resetPassword: password quality 0x"
1391 + Integer.toHexString(quality)
1392 + " does not meet required quality 0x"
1393 + Integer.toHexString(quality));
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001394 return false;
1395 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001396 quality = Math.max(realQuality, quality);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001397 }
1398 int length = getPasswordMinimumLength(null);
1399 if (password.length() < length) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001400 Slog.w(TAG, "resetPassword: password length " + password.length()
1401 + " does not meet required length " + length);
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001402 return false;
1403 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001404 if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
1405 int letters = 0;
1406 int uppercase = 0;
1407 int lowercase = 0;
1408 int numbers = 0;
1409 int symbols = 0;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001410 int nonletter = 0;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001411 for (int i = 0; i < password.length(); i++) {
1412 char c = password.charAt(i);
1413 if (c >= 'A' && c <= 'Z') {
1414 letters++;
1415 uppercase++;
1416 } else if (c >= 'a' && c <= 'z') {
1417 letters++;
1418 lowercase++;
1419 } else if (c >= '0' && c <= '9') {
1420 numbers++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001421 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001422 } else {
1423 symbols++;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001424 nonletter++;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001425 }
1426 }
1427 int neededLetters = getPasswordMinimumLetters(null);
1428 if(letters < neededLetters) {
1429 Slog.w(TAG, "resetPassword: number of letters " + letters
1430 + " does not meet required number of letters " + neededLetters);
1431 return false;
1432 }
1433 int neededNumbers = getPasswordMinimumNumeric(null);
1434 if (numbers < neededNumbers) {
1435 Slog
1436 .w(TAG, "resetPassword: number of numerical digits " + numbers
1437 + " does not meet required number of numerical digits "
1438 + neededNumbers);
1439 return false;
1440 }
1441 int neededLowerCase = getPasswordMinimumLowerCase(null);
1442 if (lowercase < neededLowerCase) {
1443 Slog.w(TAG, "resetPassword: number of lowercase letters " + lowercase
1444 + " does not meet required number of lowercase letters "
1445 + neededLowerCase);
1446 return false;
1447 }
1448 int neededUpperCase = getPasswordMinimumUpperCase(null);
1449 if (uppercase < neededUpperCase) {
1450 Slog.w(TAG, "resetPassword: number of uppercase letters " + uppercase
1451 + " does not meet required number of uppercase letters "
1452 + neededUpperCase);
1453 return false;
1454 }
1455 int neededSymbols = getPasswordMinimumSymbols(null);
1456 if (symbols < neededSymbols) {
1457 Slog.w(TAG, "resetPassword: number of special symbols " + symbols
1458 + " does not meet required number of special symbols " + neededSymbols);
1459 return false;
1460 }
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001461 int neededNonLetter = getPasswordMinimumNonLetter(null);
1462 if (nonletter < neededNonLetter) {
1463 Slog.w(TAG, "resetPassword: number of non-letter characters " + nonletter
1464 + " does not meet required number of non-letter characters "
1465 + neededNonLetter);
1466 return false;
1467 }
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001468 }
1469
1470 LockPatternUtils utils = new LockPatternUtils(mContext);
1471 if(utils.checkPasswordHistory(password)) {
1472 Slog.w(TAG, "resetPassword: password is the same as one of the last "
1473 + getPasswordHistoryLength(null) + " passwords");
1474 return false;
1475 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001476 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001477
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001478 int callingUid = Binder.getCallingUid();
1479 if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001480 Slog.w(TAG, "resetPassword: already set by another uid and not entered by user");
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001481 return false;
1482 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001483
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001484 // Don't do this with the lock held, because it is going to call
1485 // back in to the service.
1486 long ident = Binder.clearCallingIdentity();
1487 try {
1488 LockPatternUtils utils = new LockPatternUtils(mContext);
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001489 utils.saveLockPassword(password, quality);
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001490 synchronized (this) {
1491 int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
1492 != 0 ? callingUid : -1;
1493 if (mPasswordOwner != newOwner) {
1494 mPasswordOwner = newOwner;
1495 saveSettingsLocked();
1496 }
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001497 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001498 } finally {
1499 Binder.restoreCallingIdentity(ident);
1500 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001501
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001502 return true;
1503 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001504
Dianne Hackbornd6847842010-01-12 18:14:19 -08001505 public void setMaximumTimeToLock(ComponentName who, long timeMs) {
1506 synchronized (this) {
1507 if (who == null) {
1508 throw new NullPointerException("ComponentName is null");
1509 }
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001510 ActiveAdmin ap = getActiveAdminForCallerLocked(who,
Dianne Hackborn315ada72010-02-11 12:14:08 -08001511 DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001512 if (ap.maximumTimeToUnlock != timeMs) {
1513 ap.maximumTimeToUnlock = timeMs;
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001514
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001515 long ident = Binder.clearCallingIdentity();
1516 try {
1517 saveSettingsLocked();
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001518
Dianne Hackborn254cb442010-01-27 19:23:59 -08001519 timeMs = getMaximumTimeToLock(null);
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001520 if (timeMs <= 0) {
1521 timeMs = Integer.MAX_VALUE;
1522 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001523
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001524 try {
1525 getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
1526 } catch (RemoteException e) {
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001527 Slog.w(TAG, "Failure talking with power manager", e);
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001528 }
1529 } finally {
1530 Binder.restoreCallingIdentity(ident);
1531 }
Dianne Hackbornd6847842010-01-12 18:14:19 -08001532 }
1533 }
1534 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001535
Dianne Hackborn254cb442010-01-27 19:23:59 -08001536 public long getMaximumTimeToLock(ComponentName who) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001537 synchronized (this) {
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001538 long time = 0;
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001539
Dianne Hackborn254cb442010-01-27 19:23:59 -08001540 if (who != null) {
1541 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
1542 return admin != null ? admin.maximumTimeToUnlock : time;
1543 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001544
Dianne Hackborn254cb442010-01-27 19:23:59 -08001545 final int N = mAdminList.size();
Dianne Hackbornd47c6ed2010-01-27 16:21:20 -08001546 for (int i=0; i<N; i++) {
1547 ActiveAdmin admin = mAdminList.get(i);
1548 if (time == 0) {
1549 time = admin.maximumTimeToUnlock;
1550 } else if (admin.maximumTimeToUnlock != 0
1551 && time > admin.maximumTimeToUnlock) {
1552 time = admin.maximumTimeToUnlock;
1553 }
1554 }
1555 return time;
Dianne Hackbornd6847842010-01-12 18:14:19 -08001556 }
1557 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001558
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001559 public void lockNow() {
1560 synchronized (this) {
1561 // This API can only be called by an active device admin,
1562 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001563 getActiveAdminForCallerLocked(null,
1564 DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
Dianne Hackborn254cb442010-01-27 19:23:59 -08001565 long ident = Binder.clearCallingIdentity();
1566 try {
1567 mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(),
1568 WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN);
1569 } catch (RemoteException e) {
1570 } finally {
1571 Binder.restoreCallingIdentity(ident);
1572 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001573 }
1574 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001575
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001576 void wipeDataLocked(int flags) {
Dianne Hackborn42499172010-10-15 18:45:07 -07001577 if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) {
1578 Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
1579 intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
1580 mWakeLock.acquire(10000);
1581 mContext.startService(intent);
1582 } else {
1583 try {
1584 RecoverySystem.rebootWipeUserData(mContext);
1585 } catch (IOException e) {
1586 Slog.w(TAG, "Failed requesting data wipe", e);
1587 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001588 }
1589 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001590
Dianne Hackbornd6847842010-01-12 18:14:19 -08001591 public void wipeData(int flags) {
1592 synchronized (this) {
1593 // This API can only be called by an active device admin,
1594 // so try to retrieve it to check that the caller is one.
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001595 getActiveAdminForCallerLocked(null,
1596 DeviceAdminInfo.USES_POLICY_WIPE_DATA);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001597 long ident = Binder.clearCallingIdentity();
1598 try {
1599 wipeDataLocked(flags);
1600 } finally {
1601 Binder.restoreCallingIdentity(ident);
1602 }
Dianne Hackborndf83afa2010-01-20 13:37:26 -08001603 }
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001604 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001605
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001606 public void getRemoveWarning(ComponentName comp, final RemoteCallback result) {
1607 mContext.enforceCallingOrSelfPermission(
1608 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001609
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001610 synchronized (this) {
1611 ActiveAdmin admin = getActiveAdminUncheckedLocked(comp);
1612 if (admin == null) {
1613 try {
1614 result.sendResult(null);
1615 } catch (RemoteException e) {
1616 }
1617 return;
1618 }
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001619 Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001620 intent.setComponent(admin.info.getComponent());
1621 mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
1622 @Override
1623 public void onReceive(Context context, Intent intent) {
1624 try {
1625 result.sendResult(getResultExtras(false));
1626 } catch (RemoteException e) {
1627 }
1628 }
1629 }, null, Activity.RESULT_OK, null, null);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001630 }
1631 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001632
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001633 public void setActivePasswordState(int quality, int length, int letters, int uppercase,
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001634 int lowercase, int numbers, int symbols, int nonletter) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001635 mContext.enforceCallingOrSelfPermission(
1636 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001637
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001638 validateQualityConstant(quality);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001639
Dianne Hackbornd6847842010-01-12 18:14:19 -08001640 synchronized (this) {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001641 if (mActivePasswordQuality != quality || mActivePasswordLength != length
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001642 || mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters
1643 || mActivePasswordUpperCase != uppercase
1644 || mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001645 || mActivePasswordSymbols != symbols || mActivePasswordNonLetter != nonletter) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001646 long ident = Binder.clearCallingIdentity();
1647 try {
Dianne Hackborn9327f4f2010-01-29 10:38:29 -08001648 mActivePasswordQuality = quality;
Dianne Hackbornd6847842010-01-12 18:14:19 -08001649 mActivePasswordLength = length;
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001650 mActivePasswordLetters = letters;
1651 mActivePasswordLowerCase = lowercase;
1652 mActivePasswordUpperCase = uppercase;
1653 mActivePasswordNumeric = numbers;
1654 mActivePasswordSymbols = symbols;
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001655 mActivePasswordNonLetter = nonletter;
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001656 mFailedPasswordAttempts = 0;
1657 saveSettingsLocked();
Jim Millera4e28d12010-11-08 16:15:47 -08001658 updatePasswordExpirationsLocked();
Andy Stadler043116a2010-11-29 17:43:32 -08001659 setExpirationAlarmCheckLocked(mContext);
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001660 sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001661 DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001662 } finally {
1663 Binder.restoreCallingIdentity(ident);
1664 }
1665 }
1666 }
1667 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001668
Andy Stadler043116a2010-11-29 17:43:32 -08001669 /**
1670 * Called any time the device password is updated. Resets all password expiration clocks.
1671 */
Jim Millera4e28d12010-11-08 16:15:47 -08001672 private void updatePasswordExpirationsLocked() {
1673 final int N = mAdminList.size();
1674 if (N > 0) {
1675 for (int i=0; i<N; i++) {
1676 ActiveAdmin admin = mAdminList.get(i);
1677 if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
Andy Stadler043116a2010-11-29 17:43:32 -08001678 long timeout = admin.passwordExpirationTimeout;
1679 long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
1680 admin.passwordExpirationDate = expiration;
Jim Millera4e28d12010-11-08 16:15:47 -08001681 }
1682 }
1683 saveSettingsLocked();
1684 }
1685 }
1686
Dianne Hackbornd6847842010-01-12 18:14:19 -08001687 public void reportFailedPasswordAttempt() {
1688 mContext.enforceCallingOrSelfPermission(
1689 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001690
Dianne Hackbornd6847842010-01-12 18:14:19 -08001691 synchronized (this) {
1692 long ident = Binder.clearCallingIdentity();
1693 try {
1694 mFailedPasswordAttempts++;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001695 saveSettingsLocked();
Dianne Hackborn254cb442010-01-27 19:23:59 -08001696 int max = getMaximumFailedPasswordsForWipe(null);
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001697 if (max > 0 && mFailedPasswordAttempts >= max) {
1698 wipeDataLocked(0);
1699 }
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001700 sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001701 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001702 } finally {
1703 Binder.restoreCallingIdentity(ident);
1704 }
1705 }
1706 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001707
Dianne Hackbornd6847842010-01-12 18:14:19 -08001708 public void reportSuccessfulPasswordAttempt() {
1709 mContext.enforceCallingOrSelfPermission(
1710 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001711
Dianne Hackbornd6847842010-01-12 18:14:19 -08001712 synchronized (this) {
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001713 if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) {
Dianne Hackbornd6847842010-01-12 18:14:19 -08001714 long ident = Binder.clearCallingIdentity();
1715 try {
1716 mFailedPasswordAttempts = 0;
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001717 mPasswordOwner = -1;
Dianne Hackborn8ea138c2010-01-26 18:01:04 -08001718 saveSettingsLocked();
Dianne Hackbornef6b22f2010-02-16 20:38:49 -08001719 sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
Dianne Hackborn8aa2e892010-01-22 11:31:30 -08001720 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
Dianne Hackbornd6847842010-01-12 18:14:19 -08001721 } finally {
1722 Binder.restoreCallingIdentity(ident);
1723 }
1724 }
1725 }
1726 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001727
Oscar Montemayor69238c62010-08-03 10:51:06 -07001728 public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
1729 String exclusionList) {
1730 synchronized(this) {
1731 if (who == null) {
1732 throw new NullPointerException("ComponentName is null");
1733 }
1734
1735 ActiveAdmin admin = getActiveAdminForCallerLocked(who,
1736 DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
1737
1738 // Scan through active admins and find if anyone has already
1739 // set the global proxy.
1740 final int N = mAdminList.size();
1741 Set<ComponentName> compSet = mAdminMap.keySet();
1742 for (ComponentName component : compSet) {
1743 ActiveAdmin ap = mAdminMap.get(component);
1744 if ((ap.specifiesGlobalProxy) && (!component.equals(who))) {
1745 // Another admin already sets the global proxy
1746 // Return it to the caller.
1747 return component;
1748 }
1749 }
1750 if (proxySpec == null) {
1751 admin.specifiesGlobalProxy = false;
1752 admin.globalProxySpec = null;
1753 admin.globalProxyExclusionList = null;
1754 } else {
1755
1756 admin.specifiesGlobalProxy = true;
1757 admin.globalProxySpec = proxySpec;
1758 admin.globalProxyExclusionList = exclusionList;
1759 }
1760
1761 // Reset the global proxy accordingly
1762 // Do this using system permissions, as apps cannot write to secure settings
1763 long origId = Binder.clearCallingIdentity();
1764 resetGlobalProxy();
1765 Binder.restoreCallingIdentity(origId);
1766 return null;
1767 }
1768 }
1769
1770 public ComponentName getGlobalProxyAdmin() {
1771 synchronized(this) {
1772 // Scan through active admins and find if anyone has already
1773 // set the global proxy.
1774 final int N = mAdminList.size();
1775 for (int i = 0; i < N; i++) {
1776 ActiveAdmin ap = mAdminList.get(i);
1777 if (ap.specifiesGlobalProxy) {
1778 // Device admin sets the global proxy
1779 // Return it to the caller.
1780 return ap.info.getComponent();
1781 }
1782 }
1783 }
1784 // No device admin sets the global proxy.
1785 return null;
1786 }
1787
1788 private void resetGlobalProxy() {
1789 final int N = mAdminList.size();
1790 for (int i = 0; i < N; i++) {
1791 ActiveAdmin ap = mAdminList.get(i);
1792 if (ap.specifiesGlobalProxy) {
1793 saveGlobalProxy(ap.globalProxySpec, ap.globalProxyExclusionList);
1794 return;
1795 }
1796 }
1797 // No device admins defining global proxies - reset global proxy settings to none
1798 saveGlobalProxy(null, null);
1799 }
1800
1801 private void saveGlobalProxy(String proxySpec, String exclusionList) {
1802 if (exclusionList == null) {
1803 exclusionList = "";
1804 }
1805 if (proxySpec == null) {
1806 proxySpec = "";
1807 }
1808 // Remove white spaces
1809 proxySpec = proxySpec.trim();
Robert Greenwalt434203a2010-10-11 16:00:27 -07001810 String data[] = proxySpec.split(":");
1811 int proxyPort = 8080;
1812 if (data.length > 1) {
1813 try {
1814 proxyPort = Integer.parseInt(data[1]);
1815 } catch (NumberFormatException e) {}
1816 }
Oscar Montemayor69238c62010-08-03 10:51:06 -07001817 exclusionList = exclusionList.trim();
1818 ContentResolver res = mContext.getContentResolver();
Robert Greenwalt434203a2010-10-11 16:00:27 -07001819 Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, data[0]);
1820 Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, proxyPort);
1821 Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
1822 exclusionList);
Oscar Montemayor69238c62010-08-03 10:51:06 -07001823 }
1824
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001825 @Override
1826 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1827 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1828 != PackageManager.PERMISSION_GRANTED) {
1829
1830 pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid="
1831 + Binder.getCallingPid()
1832 + ", uid=" + Binder.getCallingUid());
1833 return;
1834 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001835
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001836 final Printer p = new PrintWriterPrinter(pw);
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001837
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001838 synchronized (this) {
1839 p.println("Current Device Policy Manager state:");
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001840
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001841 p.println(" Enabled Device Admins:");
1842 final int N = mAdminList.size();
1843 for (int i=0; i<N; i++) {
1844 ActiveAdmin ap = mAdminList.get(i);
1845 if (ap != null) {
1846 pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString());
1847 pw.println(":");
1848 ap.dump(" ", pw);
1849 }
1850 }
Konstantin Lopyrev32558232010-05-20 16:18:05 -07001851
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001852 pw.println(" ");
Dianne Hackborn85f2c9c2010-03-22 11:12:48 -07001853 pw.print(" mActivePasswordQuality=0x");
1854 pw.println(Integer.toHexString(mActivePasswordQuality));
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001855 pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength);
Konstantin Lopyreva15dcfa2010-05-24 17:10:56 -07001856 pw.print(" mActivePasswordUpperCase="); pw.println(mActivePasswordUpperCase);
1857 pw.print(" mActivePasswordLowerCase="); pw.println(mActivePasswordLowerCase);
1858 pw.print(" mActivePasswordLetters="); pw.println(mActivePasswordLetters);
1859 pw.print(" mActivePasswordNumeric="); pw.println(mActivePasswordNumeric);
1860 pw.print(" mActivePasswordSymbols="); pw.println(mActivePasswordSymbols);
Konstantin Lopyrevc8577402010-06-04 17:15:02 -07001861 pw.print(" mActivePasswordNonLetter="); pw.println(mActivePasswordNonLetter);
Dianne Hackborn87bba1e2010-02-26 17:25:54 -08001862 pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
1863 pw.print(" mPasswordOwner="); pw.println(mPasswordOwner);
1864 }
1865 }
Dianne Hackbornd6847842010-01-12 18:14:19 -08001866}