blob: bf2a5aec5f614570557d080747e631419d0a87e5 [file] [log] [blame]
Dianne Hackborna06de0f2012-12-11 16:34:47 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import java.io.File;
20import java.io.FileDescriptor;
Dianne Hackborn35654b62013-01-14 17:38:02 -080021import java.io.FileInputStream;
22import java.io.FileNotFoundException;
23import java.io.FileOutputStream;
24import java.io.IOException;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080025import java.io.PrintWriter;
Dianne Hackborn35654b62013-01-14 17:38:02 -080026import java.util.ArrayList;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080027import java.util.HashMap;
Dianne Hackborn35654b62013-01-14 17:38:02 -080028import java.util.List;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080029
30import android.app.AppOpsManager;
31import android.content.Context;
32import android.content.pm.PackageManager;
33import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborn35654b62013-01-14 17:38:02 -080034import android.os.AsyncTask;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080035import android.os.Binder;
Dianne Hackborn35654b62013-01-14 17:38:02 -080036import android.os.Handler;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080037import android.os.Process;
38import android.os.ServiceManager;
39import android.os.UserHandle;
40import android.util.AtomicFile;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080041import android.util.Log;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080042import android.util.Slog;
43import android.util.SparseArray;
44import android.util.TimeUtils;
Dianne Hackborn35654b62013-01-14 17:38:02 -080045import android.util.Xml;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080046
47import com.android.internal.app.IAppOpsService;
Dianne Hackborn35654b62013-01-14 17:38:02 -080048import com.android.internal.util.FastXmlSerializer;
49import com.android.internal.util.XmlUtils;
50
51import org.xmlpull.v1.XmlPullParser;
52import org.xmlpull.v1.XmlPullParserException;
53import org.xmlpull.v1.XmlSerializer;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080054
55public class AppOpsService extends IAppOpsService.Stub {
56 static final String TAG = "AppOps";
Dianne Hackborn35654b62013-01-14 17:38:02 -080057 static final boolean DEBUG = false;
58
59 // Write at most every 30 minutes.
60 static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080061
62 Context mContext;
63 final AtomicFile mFile;
Dianne Hackborn35654b62013-01-14 17:38:02 -080064 final Handler mHandler;
65
66 boolean mWriteScheduled;
67 final Runnable mWriteRunner = new Runnable() {
68 public void run() {
69 synchronized (AppOpsService.this) {
70 mWriteScheduled = false;
71 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
72 @Override protected Void doInBackground(Void... params) {
73 writeState();
74 return null;
75 }
76 };
77 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
78 }
79 }
80 };
Dianne Hackborna06de0f2012-12-11 16:34:47 -080081
82 final SparseArray<HashMap<String, Ops>> mUidOps
83 = new SparseArray<HashMap<String, Ops>>();
84
85 final static class Ops extends SparseArray<Op> {
86 public final String packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080087 public final int uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080088
Dianne Hackborn35654b62013-01-14 17:38:02 -080089 public Ops(String _packageName, int _uid) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -080090 packageName = _packageName;
Dianne Hackborn35654b62013-01-14 17:38:02 -080091 uid = _uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080092 }
93 }
94
95 final static class Op {
96 public final int op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080097 public int mode;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080098 public int duration;
99 public long time;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800100 public long rejectTime;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800101 public int nesting;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800102
103 public Op(int _op) {
104 op = _op;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800105 mode = AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800106 }
107 }
108
Dianne Hackborn35654b62013-01-14 17:38:02 -0800109 public AppOpsService(File storagePath) {
110 mFile = new AtomicFile(storagePath);
111 mHandler = new Handler();
112 readState();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800113 }
114
115 public void publish(Context context) {
116 mContext = context;
117 ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
118 }
119
120 public void shutdown() {
121 Slog.w(TAG, "Writing app ops before shutdown...");
Dianne Hackborn35654b62013-01-14 17:38:02 -0800122 boolean doWrite = false;
123 synchronized (this) {
124 if (mWriteScheduled) {
125 mWriteScheduled = false;
126 doWrite = true;
127 }
128 }
129 if (doWrite) {
130 writeState();
131 }
132 }
133
Dianne Hackborn72e39832013-01-18 18:36:09 -0800134 private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
135 ArrayList<AppOpsManager.OpEntry> resOps = null;
136 if (ops == null) {
137 resOps = new ArrayList<AppOpsManager.OpEntry>();
138 for (int j=0; j<pkgOps.size(); j++) {
139 Op curOp = pkgOps.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800140 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
141 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800142 }
143 } else {
144 for (int j=0; j<ops.length; j++) {
145 Op curOp = pkgOps.get(ops[j]);
146 if (curOp != null) {
147 if (resOps == null) {
148 resOps = new ArrayList<AppOpsManager.OpEntry>();
149 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800150 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
151 curOp.rejectTime, curOp.duration));
Dianne Hackborn72e39832013-01-18 18:36:09 -0800152 }
153 }
154 }
155 return resOps;
156 }
157
Dianne Hackborn35654b62013-01-14 17:38:02 -0800158 @Override
159 public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
160 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
161 Binder.getCallingPid(), Binder.getCallingUid(), null);
162 ArrayList<AppOpsManager.PackageOps> res = null;
163 synchronized (this) {
164 for (int i=0; i<mUidOps.size(); i++) {
165 HashMap<String, Ops> packages = mUidOps.valueAt(i);
166 for (Ops pkgOps : packages.values()) {
Dianne Hackborn72e39832013-01-18 18:36:09 -0800167 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800168 if (resOps != null) {
169 if (res == null) {
170 res = new ArrayList<AppOpsManager.PackageOps>();
171 }
172 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
173 pkgOps.packageName, pkgOps.uid, resOps);
174 res.add(resPackage);
175 }
176 }
177 }
178 }
179 return res;
180 }
181
182 @Override
Dianne Hackborn72e39832013-01-18 18:36:09 -0800183 public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
184 int[] ops) {
185 mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
186 Binder.getCallingPid(), Binder.getCallingUid(), null);
187 synchronized (this) {
188 Ops pkgOps = getOpsLocked(uid, packageName, false);
189 if (pkgOps == null) {
190 return null;
191 }
192 ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
193 if (resOps == null) {
194 return null;
195 }
196 ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
197 AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
198 pkgOps.packageName, pkgOps.uid, resOps);
199 res.add(resPackage);
200 return res;
201 }
202 }
203
204 @Override
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800205 public void setMode(int code, int uid, String packageName, int mode) {
206 uid = handleIncomingUid(uid);
207 synchronized (this) {
208 Op op = getOpLocked(code, uid, packageName, true);
209 if (op != null) {
210 if (op.mode != mode) {
211 op.mode = mode;
212 scheduleWriteNowLocked();
213 }
214 }
215 }
216 }
217
218 @Override
Dianne Hackborn35654b62013-01-14 17:38:02 -0800219 public int checkOperation(int code, int uid, String packageName) {
220 uid = handleIncomingUid(uid);
221 synchronized (this) {
222 Op op = getOpLocked(code, uid, packageName, false);
223 if (op == null) {
224 return AppOpsManager.MODE_ALLOWED;
225 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800226 return op.mode;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800227 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800228 }
229
230 @Override
231 public int noteOperation(int code, int uid, String packageName) {
232 uid = handleIncomingUid(uid);
233 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800234 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800235 if (op == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800236 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
237 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800238 return AppOpsManager.MODE_IGNORED;
239 }
240 if (op.duration == -1) {
241 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
242 + " code " + code + " time=" + op.time + " duration=" + op.duration);
243 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800244 op.duration = 0;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800245 if (op.mode != AppOpsManager.MODE_ALLOWED) {
246 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + code
247 + " uid " + uid + " package " + packageName);
248 op.rejectTime = System.currentTimeMillis();
249 return op.mode;
250 }
251 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
252 + " package " + packageName);
253 op.time = System.currentTimeMillis();
254 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800255 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800256 }
257
258 @Override
259 public int startOperation(int code, int uid, String packageName) {
260 uid = handleIncomingUid(uid);
261 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800262 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800263 if (op == null) {
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800264 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
265 + " package " + packageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800266 return AppOpsManager.MODE_IGNORED;
267 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800268 if (op.mode != AppOpsManager.MODE_ALLOWED) {
269 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code " + code
270 + " uid " + uid + " package " + packageName);
271 op.rejectTime = System.currentTimeMillis();
272 return op.mode;
273 }
274 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
275 + " package " + packageName);
Dianne Hackborn35654b62013-01-14 17:38:02 -0800276 if (op.nesting == 0) {
277 op.time = System.currentTimeMillis();
278 op.duration = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800279 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800280 op.nesting++;
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800281 return AppOpsManager.MODE_ALLOWED;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800282 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800283 }
284
285 @Override
286 public void finishOperation(int code, int uid, String packageName) {
287 uid = handleIncomingUid(uid);
288 synchronized (this) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800289 Op op = getOpLocked(code, uid, packageName, true);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800290 if (op == null) {
291 return;
292 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800293 if (op.nesting <= 1) {
294 if (op.nesting == 1) {
295 op.duration = (int)(System.currentTimeMillis() - op.time);
296 } else {
297 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
298 + " code " + code + " time=" + op.time + " duration=" + op.duration
299 + " nesting=" + op.nesting);
300 }
Dianne Hackborne7991752013-01-16 17:56:46 -0800301 op.nesting = 0;
Dianne Hackborn35654b62013-01-14 17:38:02 -0800302 } else {
303 op.nesting--;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800304 }
305 }
306 }
307
308 private int handleIncomingUid(int uid) {
309 if (uid == Binder.getCallingUid()) {
310 return uid;
311 }
312 if (Binder.getCallingPid() == Process.myPid()) {
313 return uid;
314 }
315 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
316 Binder.getCallingPid(), Binder.getCallingUid(), null);
317 return uid;
318 }
319
Dianne Hackborn72e39832013-01-18 18:36:09 -0800320 private Ops getOpsLocked(int uid, String packageName, boolean edit) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800321 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
322 if (pkgOps == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800323 if (!edit) {
324 return null;
325 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800326 pkgOps = new HashMap<String, Ops>();
327 mUidOps.put(uid, pkgOps);
328 }
329 Ops ops = pkgOps.get(packageName);
330 if (ops == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800331 if (!edit) {
332 return null;
333 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800334 // This is the first time we have seen this package name under this uid,
335 // so let's make sure it is valid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800336 final long ident = Binder.clearCallingIdentity();
337 try {
338 int pkgUid = -1;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800339 try {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800340 pkgUid = mContext.getPackageManager().getPackageUid(packageName,
341 UserHandle.getUserId(uid));
342 } catch (NameNotFoundException e) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800343 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800344 if (pkgUid != uid) {
345 // Oops! The package name is not valid for the uid they are calling
346 // under. Abort.
347 Slog.w(TAG, "Bad call: specified package " + packageName
348 + " under uid " + uid + " but it is really " + pkgUid);
349 return null;
350 }
351 } finally {
352 Binder.restoreCallingIdentity(ident);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800353 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800354 ops = new Ops(packageName, uid);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800355 pkgOps.put(packageName, ops);
356 }
Dianne Hackborn72e39832013-01-18 18:36:09 -0800357 return ops;
358 }
359
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800360 private void scheduleWriteLocked() {
361 if (!mWriteScheduled) {
362 mWriteScheduled = true;
363 mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
364 }
365 }
366
367 private void scheduleWriteNowLocked() {
368 if (!mWriteScheduled) {
369 mWriteScheduled = true;
370 }
371 mHandler.removeCallbacks(mWriteRunner);
372 mHandler.post(mWriteRunner);
373 }
374
Dianne Hackborn72e39832013-01-18 18:36:09 -0800375 private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
376 Ops ops = getOpsLocked(uid, packageName, edit);
377 if (ops == null) {
378 return null;
379 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800380 Op op = ops.get(code);
381 if (op == null) {
Dianne Hackborn35654b62013-01-14 17:38:02 -0800382 if (!edit) {
383 return null;
384 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800385 op = new Op(code);
386 ops.put(code, op);
387 }
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800388 if (edit) {
389 scheduleWriteLocked();
Dianne Hackborn35654b62013-01-14 17:38:02 -0800390 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800391 return op;
392 }
393
Dianne Hackborn35654b62013-01-14 17:38:02 -0800394 void readState() {
395 synchronized (mFile) {
396 synchronized (this) {
397 FileInputStream stream;
398 try {
399 stream = mFile.openRead();
400 } catch (FileNotFoundException e) {
401 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
402 return;
403 }
404 boolean success = false;
405 try {
406 XmlPullParser parser = Xml.newPullParser();
407 parser.setInput(stream, null);
408 int type;
409 while ((type = parser.next()) != XmlPullParser.START_TAG
410 && type != XmlPullParser.END_DOCUMENT) {
411 ;
412 }
413
414 if (type != XmlPullParser.START_TAG) {
415 throw new IllegalStateException("no start tag found");
416 }
417
418 int outerDepth = parser.getDepth();
419 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
420 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
421 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
422 continue;
423 }
424
425 String tagName = parser.getName();
426 if (tagName.equals("pkg")) {
427 readPackage(parser);
428 } else {
429 Slog.w(TAG, "Unknown element under <app-ops>: "
430 + parser.getName());
431 XmlUtils.skipCurrentTag(parser);
432 }
433 }
434 success = true;
435 } catch (IllegalStateException e) {
436 Slog.w(TAG, "Failed parsing " + e);
437 } catch (NullPointerException e) {
438 Slog.w(TAG, "Failed parsing " + e);
439 } catch (NumberFormatException e) {
440 Slog.w(TAG, "Failed parsing " + e);
441 } catch (XmlPullParserException e) {
442 Slog.w(TAG, "Failed parsing " + e);
443 } catch (IOException e) {
444 Slog.w(TAG, "Failed parsing " + e);
445 } catch (IndexOutOfBoundsException e) {
446 Slog.w(TAG, "Failed parsing " + e);
447 } finally {
448 if (!success) {
449 mUidOps.clear();
450 }
451 try {
452 stream.close();
453 } catch (IOException e) {
454 }
455 }
456 }
457 }
458 }
459
460 void readPackage(XmlPullParser parser) throws NumberFormatException,
461 XmlPullParserException, IOException {
462 String pkgName = parser.getAttributeValue(null, "n");
463 int outerDepth = parser.getDepth();
464 int type;
465 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
466 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
467 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
468 continue;
469 }
470
471 String tagName = parser.getName();
472 if (tagName.equals("uid")) {
473 readUid(parser, pkgName);
474 } else {
475 Slog.w(TAG, "Unknown element under <pkg>: "
476 + parser.getName());
477 XmlUtils.skipCurrentTag(parser);
478 }
479 }
480 }
481
482 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
483 XmlPullParserException, IOException {
484 int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
485 int outerDepth = parser.getDepth();
486 int type;
487 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
488 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
489 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
490 continue;
491 }
492
493 String tagName = parser.getName();
494 if (tagName.equals("op")) {
495 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800496 String mode = parser.getAttributeValue(null, "m");
497 if (mode != null) {
498 op.mode = Integer.parseInt(mode);
499 }
500 String time = parser.getAttributeValue(null, "t");
501 if (time != null) {
502 op.time = Long.parseLong(time);
503 }
504 time = parser.getAttributeValue(null, "r");
505 if (time != null) {
506 op.rejectTime = Long.parseLong(time);
507 }
508 String dur = parser.getAttributeValue(null, "d");
509 if (dur != null) {
510 op.duration = Integer.parseInt(dur);
511 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800512 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
513 if (pkgOps == null) {
514 pkgOps = new HashMap<String, Ops>();
515 mUidOps.put(uid, pkgOps);
516 }
517 Ops ops = pkgOps.get(pkgName);
518 if (ops == null) {
519 ops = new Ops(pkgName, uid);
520 pkgOps.put(pkgName, ops);
521 }
522 ops.put(op.op, op);
523 } else {
524 Slog.w(TAG, "Unknown element under <pkg>: "
525 + parser.getName());
526 XmlUtils.skipCurrentTag(parser);
527 }
528 }
529 }
530
531 void writeState() {
532 synchronized (mFile) {
533 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
534
535 FileOutputStream stream;
536 try {
537 stream = mFile.startWrite();
538 } catch (IOException e) {
539 Slog.w(TAG, "Failed to write state: " + e);
540 return;
541 }
542
543 try {
544 XmlSerializer out = new FastXmlSerializer();
545 out.setOutput(stream, "utf-8");
546 out.startDocument(null, true);
547 out.startTag(null, "app-ops");
548
549 if (allOps != null) {
550 String lastPkg = null;
551 for (int i=0; i<allOps.size(); i++) {
552 AppOpsManager.PackageOps pkg = allOps.get(i);
553 if (!pkg.getPackageName().equals(lastPkg)) {
554 if (lastPkg != null) {
555 out.endTag(null, "pkg");
556 }
557 lastPkg = pkg.getPackageName();
558 out.startTag(null, "pkg");
559 out.attribute(null, "n", lastPkg);
560 }
561 out.startTag(null, "uid");
562 out.attribute(null, "n", Integer.toString(pkg.getUid()));
563 List<AppOpsManager.OpEntry> ops = pkg.getOps();
564 for (int j=0; j<ops.size(); j++) {
565 AppOpsManager.OpEntry op = ops.get(j);
566 out.startTag(null, "op");
567 out.attribute(null, "n", Integer.toString(op.getOp()));
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800568 if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
569 out.attribute(null, "m", Integer.toString(op.getMode()));
570 }
571 long time = op.getTime();
572 if (time != 0) {
573 out.attribute(null, "t", Long.toString(time));
574 }
575 time = op.getRejectTime();
576 if (time != 0) {
577 out.attribute(null, "r", Long.toString(time));
578 }
579 int dur = op.getDuration();
580 if (dur != 0) {
581 out.attribute(null, "d", Integer.toString(dur));
582 }
Dianne Hackborn35654b62013-01-14 17:38:02 -0800583 out.endTag(null, "op");
584 }
585 out.endTag(null, "uid");
586 }
587 if (lastPkg != null) {
588 out.endTag(null, "pkg");
589 }
590 }
591
592 out.endTag(null, "app-ops");
593 out.endDocument();
594 mFile.finishWrite(stream);
595 } catch (IOException e) {
596 Slog.w(TAG, "Failed to write state, restoring backup.", e);
597 mFile.failWrite(stream);
598 }
599 }
600 }
601
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800602 @Override
603 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
604 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
605 != PackageManager.PERMISSION_GRANTED) {
606 pw.println("Permission Denial: can't dump ApOps service from from pid="
607 + Binder.getCallingPid()
608 + ", uid=" + Binder.getCallingUid());
609 return;
610 }
611
612 synchronized (this) {
613 pw.println("Current AppOps Service state:");
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800614 final long now = System.currentTimeMillis();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800615 for (int i=0; i<mUidOps.size(); i++) {
616 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
617 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
618 for (Ops ops : pkgOps.values()) {
619 pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
620 for (int j=0; j<ops.size(); j++) {
621 Op op = ops.valueAt(j);
Dianne Hackborn5e45ee62013-01-24 19:13:44 -0800622 pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
623 pw.print(": mode="); pw.print(op.mode);
624 if (op.time != 0) {
625 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
626 pw.print(" ago");
627 }
628 if (op.rejectTime != 0) {
629 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
630 pw.print(" ago");
631 }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800632 if (op.duration == -1) {
633 pw.println(" (running)");
634 } else {
635 pw.print("; duration=");
636 TimeUtils.formatDuration(op.duration, pw);
637 pw.println();
638 }
639 }
640 }
641 }
642 }
643 }
644}