OSDN Git Service

Revert "Add version identifier to app ops."
[android-x86/frameworks-base.git] / services / java / com / android / server / AppOpsService.java
1 /*
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
17 package com.android.server;
18
19 import java.io.File;
20 import java.io.FileDescriptor;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.List;
30
31 import android.app.AppOpsManager;
32 import android.content.Context;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.os.AsyncTask;
36 import android.os.Binder;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.Process;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.os.UserHandle;
43 import android.util.AtomicFile;
44 import android.util.Log;
45 import android.util.Slog;
46 import android.util.SparseArray;
47 import android.util.TimeUtils;
48 import android.util.Xml;
49
50 import com.android.internal.app.IAppOpsService;
51 import com.android.internal.app.IAppOpsCallback;
52 import com.android.internal.util.FastXmlSerializer;
53 import com.android.internal.util.XmlUtils;
54
55 import org.xmlpull.v1.XmlPullParser;
56 import org.xmlpull.v1.XmlPullParserException;
57 import org.xmlpull.v1.XmlSerializer;
58
59 public class AppOpsService extends IAppOpsService.Stub {
60     static final String TAG = "AppOps";
61     static final boolean DEBUG = false;
62
63     // Write at most every 30 minutes.
64     static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
65
66     Context mContext;
67     final AtomicFile mFile;
68     final Handler mHandler;
69
70     boolean mWriteScheduled;
71     final Runnable mWriteRunner = new Runnable() {
72         public void run() {
73             synchronized (AppOpsService.this) {
74                 mWriteScheduled = false;
75                 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
76                     @Override protected Void doInBackground(Void... params) {
77                         writeState();
78                         return null;
79                     }
80                 };
81                 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
82             }
83         }
84     };
85
86     final SparseArray<HashMap<String, Ops>> mUidOps
87             = new SparseArray<HashMap<String, Ops>>();
88
89     public final static class Ops extends SparseArray<Op> {
90         public final String packageName;
91         public final int uid;
92
93         public Ops(String _packageName, int _uid) {
94             packageName = _packageName;
95             uid = _uid;
96         }
97     }
98
99     public final static class Op {
100         public final int op;
101         public int mode;
102         public int duration;
103         public long time;
104         public long rejectTime;
105         public int nesting;
106
107         public Op(int _op) {
108             op = _op;
109             mode = AppOpsManager.MODE_ALLOWED;
110         }
111     }
112
113     final SparseArray<ArrayList<Callback>> mOpModeWatchers
114             = new SparseArray<ArrayList<Callback>>();
115     final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
116             = new HashMap<String, ArrayList<Callback>>();
117     final HashMap<IBinder, Callback> mModeWatchers
118             = new HashMap<IBinder, Callback>();
119
120     public final class Callback implements DeathRecipient {
121         final IAppOpsCallback mCallback;
122
123         public Callback(IAppOpsCallback callback) {
124             mCallback = callback;
125             try {
126                 mCallback.asBinder().linkToDeath(this, 0);
127             } catch (RemoteException e) {
128             }
129         }
130
131         public void unlinkToDeath() {
132             mCallback.asBinder().unlinkToDeath(this, 0);
133         }
134
135         @Override
136         public void binderDied() {
137             stopWatchingMode(mCallback);
138         }
139     }
140
141     public AppOpsService(File storagePath) {
142         mFile = new AtomicFile(storagePath);
143         mHandler = new Handler();
144         readState();
145     }
146     
147     public void publish(Context context) {
148         mContext = context;
149         ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
150     }
151
152     public void systemReady() {
153         synchronized (this) {
154             boolean changed = false;
155             for (int i=0; i<mUidOps.size(); i++) {
156                 HashMap<String, Ops> pkgs = mUidOps.valueAt(i);
157                 Iterator<Ops> it = pkgs.values().iterator();
158                 while (it.hasNext()) {
159                     Ops ops = it.next();
160                     int curUid;
161                     try {
162                         curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
163                                 UserHandle.getUserId(ops.uid));
164                     } catch (NameNotFoundException e) {
165                         curUid = -1;
166                     }
167                     if (curUid != ops.uid) {
168                         Slog.i(TAG, "Pruning old package " + ops.packageName
169                                 + "/" + ops.uid + ": new uid=" + curUid);
170                         it.remove();
171                         changed = true;
172                     }
173                 }
174                 if (pkgs.size() <= 0) {
175                     mUidOps.removeAt(i);
176                 }
177             }
178             if (changed) {
179                 scheduleWriteLocked();
180             }
181         }
182     }
183
184     public void packageRemoved(int uid, String packageName) {
185         synchronized (this) {
186             HashMap<String, Ops> pkgs = mUidOps.get(uid);
187             if (pkgs != null) {
188                 if (pkgs.remove(packageName) != null) {
189                     if (pkgs.size() <= 0) {
190                         mUidOps.remove(uid);
191                     }
192                     scheduleWriteLocked();
193                 }
194             }
195         }
196     }
197
198     public void uidRemoved(int uid) {
199         synchronized (this) {
200             if (mUidOps.indexOfKey(uid) >= 0) {
201                 mUidOps.remove(uid);
202                 scheduleWriteLocked();
203             }
204         }
205     }
206
207     public void shutdown() {
208         Slog.w(TAG, "Writing app ops before shutdown...");
209         boolean doWrite = false;
210         synchronized (this) {
211             if (mWriteScheduled) {
212                 mWriteScheduled = false;
213                 doWrite = true;
214             }
215         }
216         if (doWrite) {
217             writeState();
218         }
219     }
220
221     private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
222         ArrayList<AppOpsManager.OpEntry> resOps = null;
223         if (ops == null) {
224             resOps = new ArrayList<AppOpsManager.OpEntry>();
225             for (int j=0; j<pkgOps.size(); j++) {
226                 Op curOp = pkgOps.valueAt(j);
227                 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
228                         curOp.rejectTime, curOp.duration));
229             }
230         } else {
231             for (int j=0; j<ops.length; j++) {
232                 Op curOp = pkgOps.get(ops[j]);
233                 if (curOp != null) {
234                     if (resOps == null) {
235                         resOps = new ArrayList<AppOpsManager.OpEntry>();
236                     }
237                     resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
238                             curOp.rejectTime, curOp.duration));
239                 }
240             }
241         }
242         return resOps;
243     }
244
245     @Override
246     public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
247         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
248                 Binder.getCallingPid(), Binder.getCallingUid(), null);
249         ArrayList<AppOpsManager.PackageOps> res = null;
250         synchronized (this) {
251             for (int i=0; i<mUidOps.size(); i++) {
252                 HashMap<String, Ops> packages = mUidOps.valueAt(i);
253                 for (Ops pkgOps : packages.values()) {
254                     ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
255                     if (resOps != null) {
256                         if (res == null) {
257                             res = new ArrayList<AppOpsManager.PackageOps>();
258                         }
259                         AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
260                                 pkgOps.packageName, pkgOps.uid, resOps);
261                         res.add(resPackage);
262                     }
263                 }
264             }
265         }
266         return res;
267     }
268
269     @Override
270     public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
271             int[] ops) {
272         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
273                 Binder.getCallingPid(), Binder.getCallingUid(), null);
274         synchronized (this) {
275             Ops pkgOps = getOpsLocked(uid, packageName, false);
276             if (pkgOps == null) {
277                 return null;
278             }
279             ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
280             if (resOps == null) {
281                 return null;
282             }
283             ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
284             AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
285                     pkgOps.packageName, pkgOps.uid, resOps);
286             res.add(resPackage);
287             return res;
288         }
289     }
290
291     @Override
292     public void setMode(int code, int uid, String packageName, int mode) {
293         verifyIncomingUid(uid);
294         verifyIncomingOp(code);
295         ArrayList<Callback> repCbs = null;
296         code = AppOpsManager.opToSwitch(code);
297         synchronized (this) {
298             Op op = getOpLocked(code, uid, packageName, true);
299             if (op != null) {
300                 if (op.mode != mode) {
301                     op.mode = mode;
302                     ArrayList<Callback> cbs = mOpModeWatchers.get(code);
303                     if (cbs != null) {
304                         if (repCbs == null) {
305                             repCbs = new ArrayList<Callback>();
306                         }
307                         repCbs.addAll(cbs);
308                     }
309                     cbs = mPackageModeWatchers.get(packageName);
310                     if (cbs != null) {
311                         if (repCbs == null) {
312                             repCbs = new ArrayList<Callback>();
313                         }
314                         repCbs.addAll(cbs);
315                     }
316                     if (mode == AppOpsManager.MODE_ALLOWED) {
317                         // If going into the default mode, prune this op
318                         // if there is nothing else interesting in it.
319                         if (op.time == 0 && op.rejectTime == 0) {
320                             Ops ops = getOpsLocked(uid, packageName, false);
321                             if (ops != null) {
322                                 ops.remove(op.op);
323                                 if (ops.size() <= 0) {
324                                     HashMap<String, Ops> pkgOps = mUidOps.get(uid);
325                                     if (pkgOps != null) {
326                                         pkgOps.remove(ops.packageName);
327                                         if (pkgOps.size() <= 0) {
328                                             mUidOps.remove(uid);
329                                         }
330                                     }
331                                 }
332                             }
333                         }
334                     }
335                     scheduleWriteNowLocked();
336                 }
337             }
338         }
339         if (repCbs != null) {
340             for (int i=0; i<repCbs.size(); i++) {
341                 try {
342                     repCbs.get(i).mCallback.opChanged(code, packageName);
343                 } catch (RemoteException e) {
344                 }
345             }
346         }
347     }
348
349     @Override
350     public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
351         synchronized (this) {
352             op = AppOpsManager.opToSwitch(op);
353             Callback cb = mModeWatchers.get(callback.asBinder());
354             if (cb == null) {
355                 cb = new Callback(callback);
356                 mModeWatchers.put(callback.asBinder(), cb);
357             }
358             if (op != AppOpsManager.OP_NONE) {
359                 ArrayList<Callback> cbs = mOpModeWatchers.get(op);
360                 if (cbs == null) {
361                     cbs = new ArrayList<Callback>();
362                     mOpModeWatchers.put(op, cbs);
363                 }
364                 cbs.add(cb);
365             }
366             if (packageName != null) {
367                 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
368                 if (cbs == null) {
369                     cbs = new ArrayList<Callback>();
370                     mPackageModeWatchers.put(packageName, cbs);
371                 }
372                 cbs.add(cb);
373             }
374         }
375     }
376
377     @Override
378     public void stopWatchingMode(IAppOpsCallback callback) {
379         synchronized (this) {
380             Callback cb = mModeWatchers.remove(callback.asBinder());
381             if (cb != null) {
382                 cb.unlinkToDeath();
383                 for (int i=0; i<mOpModeWatchers.size(); i++) {
384                     ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
385                     cbs.remove(cb);
386                     if (cbs.size() <= 0) {
387                         mOpModeWatchers.removeAt(i);
388                     }
389                 }
390                 if (mPackageModeWatchers.size() > 0) {
391                     Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
392                     while (it.hasNext()) {
393                         ArrayList<Callback> cbs = it.next();
394                         cbs.remove(cb);
395                         if (cbs.size() <= 0) {
396                             it.remove();
397                         }
398                     }
399                 }
400             }
401         }
402     }
403
404     @Override
405     public int checkOperation(int code, int uid, String packageName) {
406         verifyIncomingUid(uid);
407         verifyIncomingOp(code);
408         synchronized (this) {
409             Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
410             if (op == null) {
411                 return AppOpsManager.MODE_ALLOWED;
412             }
413             return op.mode;
414         }
415     }
416
417     @Override
418     public int noteOperation(int code, int uid, String packageName) {
419         verifyIncomingUid(uid);
420         verifyIncomingOp(code);
421         synchronized (this) {
422             Ops ops = getOpsLocked(uid, packageName, true);
423             if (ops == null) {
424                 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
425                         + " package " + packageName);
426                 return AppOpsManager.MODE_IGNORED;
427             }
428             Op op = getOpLocked(ops, code, true);
429             if (op.duration == -1) {
430                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
431                         + " code " + code + " time=" + op.time + " duration=" + op.duration);
432             }
433             op.duration = 0;
434             final int switchCode = AppOpsManager.opToSwitch(code);
435             final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
436             if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
437                 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
438                         + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
439                 op.rejectTime = System.currentTimeMillis();
440                 return switchOp.mode;
441             }
442             if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
443                     + " package " + packageName);
444             op.time = System.currentTimeMillis();
445             op.rejectTime = 0;
446             return AppOpsManager.MODE_ALLOWED;
447         }
448     }
449
450     @Override
451     public int startOperation(int code, int uid, String packageName) {
452         verifyIncomingUid(uid);
453         verifyIncomingOp(code);
454         synchronized (this) {
455             Ops ops = getOpsLocked(uid, packageName, true);
456             if (ops == null) {
457                 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
458                         + " package " + packageName);
459                 return AppOpsManager.MODE_IGNORED;
460             }
461             Op op = getOpLocked(ops, code, true);
462             final int switchCode = AppOpsManager.opToSwitch(code);
463             final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
464             if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
465                 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
466                         + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
467                 op.rejectTime = System.currentTimeMillis();
468                 return switchOp.mode;
469             }
470             if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
471                     + " package " + packageName);
472             if (op.nesting == 0) {
473                 op.time = System.currentTimeMillis();
474                 op.rejectTime = 0;
475                 op.duration = -1;
476             }
477             op.nesting++;
478             return AppOpsManager.MODE_ALLOWED;
479         }
480     }
481
482     @Override
483     public void finishOperation(int code, int uid, String packageName) {
484         verifyIncomingUid(uid);
485         verifyIncomingOp(code);
486         synchronized (this) {
487             Op op = getOpLocked(code, uid, packageName, true);
488             if (op == null) {
489                 return;
490             }
491             if (op.nesting <= 1) {
492                 if (op.nesting == 1) {
493                     op.duration = (int)(System.currentTimeMillis() - op.time);
494                     op.time += op.duration;
495                 } else {
496                     Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
497                         + " code " + code + " time=" + op.time + " duration=" + op.duration
498                         + " nesting=" + op.nesting);
499                 }
500                 op.nesting = 0;
501             } else {
502                 op.nesting--;
503             }
504         }
505     }
506
507     private void verifyIncomingUid(int uid) {
508         if (uid == Binder.getCallingUid()) {
509             return;
510         }
511         if (Binder.getCallingPid() == Process.myPid()) {
512             return;
513         }
514         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
515                 Binder.getCallingPid(), Binder.getCallingUid(), null);
516     }
517
518     private void verifyIncomingOp(int op) {
519         if (op >= 0 && op < AppOpsManager._NUM_OP) {
520             return;
521         }
522         throw new IllegalArgumentException("Bad operation #" + op);
523     }
524
525     private Ops getOpsLocked(int uid, String packageName, boolean edit) {
526         HashMap<String, Ops> pkgOps = mUidOps.get(uid);
527         if (pkgOps == null) {
528             if (!edit) {
529                 return null;
530             }
531             pkgOps = new HashMap<String, Ops>();
532             mUidOps.put(uid, pkgOps);
533         }
534         if (uid == 0) {
535             packageName = "root";
536         } else if (uid == Process.SHELL_UID) {
537             packageName = "com.android.shell";
538         }
539         Ops ops = pkgOps.get(packageName);
540         if (ops == null) {
541             if (!edit) {
542                 return null;
543             }
544             // This is the first time we have seen this package name under this uid,
545             // so let's make sure it is valid.
546             if (uid != 0) {
547                 final long ident = Binder.clearCallingIdentity();
548                 try {
549                     int pkgUid = -1;
550                     try {
551                         pkgUid = mContext.getPackageManager().getPackageUid(packageName,
552                                 UserHandle.getUserId(uid));
553                     } catch (NameNotFoundException e) {
554                     }
555                     if (pkgUid != uid) {
556                         // Oops!  The package name is not valid for the uid they are calling
557                         // under.  Abort.
558                         Slog.w(TAG, "Bad call: specified package " + packageName
559                                 + " under uid " + uid + " but it is really " + pkgUid);
560                         return null;
561                     }
562                 } finally {
563                     Binder.restoreCallingIdentity(ident);
564                 }
565             }
566             ops = new Ops(packageName, uid);
567             pkgOps.put(packageName, ops);
568         }
569         return ops;
570     }
571
572     private void scheduleWriteLocked() {
573         if (!mWriteScheduled) {
574             mWriteScheduled = true;
575             mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
576         }
577     }
578
579     private void scheduleWriteNowLocked() {
580         if (!mWriteScheduled) {
581             mWriteScheduled = true;
582         }
583         mHandler.removeCallbacks(mWriteRunner);
584         mHandler.post(mWriteRunner);
585     }
586
587     private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
588         Ops ops = getOpsLocked(uid, packageName, edit);
589         if (ops == null) {
590             return null;
591         }
592         return getOpLocked(ops, code, edit);
593     }
594
595     private Op getOpLocked(Ops ops, int code, boolean edit) {
596         Op op = ops.get(code);
597         if (op == null) {
598             if (!edit) {
599                 return null;
600             }
601             op = new Op(code);
602             ops.put(code, op);
603         }
604         if (edit) {
605             scheduleWriteLocked();
606         }
607         return op;
608     }
609
610     void readState() {
611         synchronized (mFile) {
612             synchronized (this) {
613                 FileInputStream stream;
614                 try {
615                     stream = mFile.openRead();
616                 } catch (FileNotFoundException e) {
617                     Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
618                     return;
619                 }
620                 boolean success = false;
621                 try {
622                     XmlPullParser parser = Xml.newPullParser();
623                     parser.setInput(stream, null);
624                     int type;
625                     while ((type = parser.next()) != XmlPullParser.START_TAG
626                             && type != XmlPullParser.END_DOCUMENT) {
627                         ;
628                     }
629
630                     if (type != XmlPullParser.START_TAG) {
631                         throw new IllegalStateException("no start tag found");
632                     }
633
634                     int outerDepth = parser.getDepth();
635                     while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
636                             && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
637                         if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
638                             continue;
639                         }
640
641                         String tagName = parser.getName();
642                         if (tagName.equals("pkg")) {
643                             readPackage(parser);
644                         } else {
645                             Slog.w(TAG, "Unknown element under <app-ops>: "
646                                     + parser.getName());
647                             XmlUtils.skipCurrentTag(parser);
648                         }
649                     }
650                     success = true;
651                 } catch (IllegalStateException e) {
652                     Slog.w(TAG, "Failed parsing " + e);
653                 } catch (NullPointerException e) {
654                     Slog.w(TAG, "Failed parsing " + e);
655                 } catch (NumberFormatException e) {
656                     Slog.w(TAG, "Failed parsing " + e);
657                 } catch (XmlPullParserException e) {
658                     Slog.w(TAG, "Failed parsing " + e);
659                 } catch (IOException e) {
660                     Slog.w(TAG, "Failed parsing " + e);
661                 } catch (IndexOutOfBoundsException e) {
662                     Slog.w(TAG, "Failed parsing " + e);
663                 } finally {
664                     if (!success) {
665                         mUidOps.clear();
666                     }
667                     try {
668                         stream.close();
669                     } catch (IOException e) {
670                     }
671                 }
672             }
673         }
674     }
675
676     void readPackage(XmlPullParser parser) throws NumberFormatException,
677             XmlPullParserException, IOException {
678         String pkgName = parser.getAttributeValue(null, "n");
679         int outerDepth = parser.getDepth();
680         int type;
681         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
682                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
683             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
684                 continue;
685             }
686
687             String tagName = parser.getName();
688             if (tagName.equals("uid")) {
689                 readUid(parser, pkgName);
690             } else {
691                 Slog.w(TAG, "Unknown element under <pkg>: "
692                         + parser.getName());
693                 XmlUtils.skipCurrentTag(parser);
694             }
695         }
696     }
697
698     void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
699             XmlPullParserException, IOException {
700         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
701         int outerDepth = parser.getDepth();
702         int type;
703         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
704                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
705             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
706                 continue;
707             }
708
709             String tagName = parser.getName();
710             if (tagName.equals("op")) {
711                 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
712                 String mode = parser.getAttributeValue(null, "m");
713                 if (mode != null) {
714                     op.mode = Integer.parseInt(mode);
715                 }
716                 String time = parser.getAttributeValue(null, "t");
717                 if (time != null) {
718                     op.time = Long.parseLong(time);
719                 }
720                 time = parser.getAttributeValue(null, "r");
721                 if (time != null) {
722                     op.rejectTime = Long.parseLong(time);
723                 }
724                 String dur = parser.getAttributeValue(null, "d");
725                 if (dur != null) {
726                     op.duration = Integer.parseInt(dur);
727                 }
728                 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
729                 if (pkgOps == null) {
730                     pkgOps = new HashMap<String, Ops>();
731                     mUidOps.put(uid, pkgOps);
732                 }
733                 Ops ops = pkgOps.get(pkgName);
734                 if (ops == null) {
735                     ops = new Ops(pkgName, uid);
736                     pkgOps.put(pkgName, ops);
737                 }
738                 ops.put(op.op, op);
739             } else {
740                 Slog.w(TAG, "Unknown element under <pkg>: "
741                         + parser.getName());
742                 XmlUtils.skipCurrentTag(parser);
743             }
744         }
745     }
746
747     void writeState() {
748         synchronized (mFile) {
749             List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
750
751             FileOutputStream stream;
752             try {
753                 stream = mFile.startWrite();
754             } catch (IOException e) {
755                 Slog.w(TAG, "Failed to write state: " + e);
756                 return;
757             }
758
759             try {
760                 XmlSerializer out = new FastXmlSerializer();
761                 out.setOutput(stream, "utf-8");
762                 out.startDocument(null, true);
763                 out.startTag(null, "app-ops");
764
765                 if (allOps != null) {
766                     String lastPkg = null;
767                     for (int i=0; i<allOps.size(); i++) {
768                         AppOpsManager.PackageOps pkg = allOps.get(i);
769                         if (!pkg.getPackageName().equals(lastPkg)) {
770                             if (lastPkg != null) {
771                                 out.endTag(null, "pkg");
772                             }
773                             lastPkg = pkg.getPackageName();
774                             out.startTag(null, "pkg");
775                             out.attribute(null, "n", lastPkg);
776                         }
777                         out.startTag(null, "uid");
778                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
779                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
780                         for (int j=0; j<ops.size(); j++) {
781                             AppOpsManager.OpEntry op = ops.get(j);
782                             out.startTag(null, "op");
783                             out.attribute(null, "n", Integer.toString(op.getOp()));
784                             if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
785                                 out.attribute(null, "m", Integer.toString(op.getMode()));
786                             }
787                             long time = op.getTime();
788                             if (time != 0) {
789                                 out.attribute(null, "t", Long.toString(time));
790                             }
791                             time = op.getRejectTime();
792                             if (time != 0) {
793                                 out.attribute(null, "r", Long.toString(time));
794                             }
795                             int dur = op.getDuration();
796                             if (dur != 0) {
797                                 out.attribute(null, "d", Integer.toString(dur));
798                             }
799                             out.endTag(null, "op");
800                         }
801                         out.endTag(null, "uid");
802                     }
803                     if (lastPkg != null) {
804                         out.endTag(null, "pkg");
805                     }
806                 }
807
808                 out.endTag(null, "app-ops");
809                 out.endDocument();
810                 mFile.finishWrite(stream);
811             } catch (IOException e) {
812                 Slog.w(TAG, "Failed to write state, restoring backup.", e);
813                 mFile.failWrite(stream);
814             }
815         }
816     }
817
818     @Override
819     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
820         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
821                 != PackageManager.PERMISSION_GRANTED) {
822             pw.println("Permission Denial: can't dump ApOps service from from pid="
823                     + Binder.getCallingPid()
824                     + ", uid=" + Binder.getCallingUid());
825             return;
826         }
827
828         synchronized (this) {
829             pw.println("Current AppOps Service state:");
830             final long now = System.currentTimeMillis();
831             for (int i=0; i<mUidOps.size(); i++) {
832                 pw.print("  Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
833                 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
834                 for (Ops ops : pkgOps.values()) {
835                     pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
836                     for (int j=0; j<ops.size(); j++) {
837                         Op op = ops.valueAt(j);
838                         pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
839                         pw.print(": mode="); pw.print(op.mode);
840                         if (op.time != 0) {
841                             pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
842                             pw.print(" ago");
843                         }
844                         if (op.rejectTime != 0) {
845                             pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
846                             pw.print(" ago");
847                         }
848                         if (op.duration == -1) {
849                             pw.println(" (running)");
850                         } else {
851                             pw.print("; duration=");
852                                     TimeUtils.formatDuration(op.duration, pw);
853                                     pw.println();
854                         }
855                     }
856                 }
857             }
858         }
859     }
860 }