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