AppOpsService.java revision 6f357d3284a833cc50a990e14b39f389b8972254
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, Handler handler) {
190        mFile = new AtomicFile(storagePath);
191        mHandler = 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 (AppOpsManager.opAllowsReset(curOp.op)
439                                && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
440                            curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
441                            changed = true;
442                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
443                                    mOpModeWatchers.get(curOp.op));
444                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
445                                    mPackageModeWatchers.get(packageName));
446                            if (curOp.time == 0 && curOp.rejectTime == 0) {
447                                pkgOps.removeAt(j);
448                            }
449                        }
450                    }
451                    if (pkgOps.size() == 0) {
452                        it.remove();
453                    }
454                }
455                if (packages.size() == 0) {
456                    mUidOps.removeAt(i);
457                }
458            }
459            if (changed) {
460                scheduleWriteNowLocked();
461            }
462        }
463        if (callbacks != null) {
464            for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
465                Callback cb = ent.getKey();
466                ArrayList<Pair<String, Integer>> reports = ent.getValue();
467                for (int i=0; i<reports.size(); i++) {
468                    Pair<String, Integer> rep = reports.get(i);
469                    try {
470                        cb.mCallback.opChanged(rep.second, rep.first);
471                    } catch (RemoteException e) {
472                    }
473                }
474            }
475        }
476    }
477
478    @Override
479    public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
480        synchronized (this) {
481            op = AppOpsManager.opToSwitch(op);
482            Callback cb = mModeWatchers.get(callback.asBinder());
483            if (cb == null) {
484                cb = new Callback(callback);
485                mModeWatchers.put(callback.asBinder(), cb);
486            }
487            if (op != AppOpsManager.OP_NONE) {
488                ArrayList<Callback> cbs = mOpModeWatchers.get(op);
489                if (cbs == null) {
490                    cbs = new ArrayList<Callback>();
491                    mOpModeWatchers.put(op, cbs);
492                }
493                cbs.add(cb);
494            }
495            if (packageName != null) {
496                ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
497                if (cbs == null) {
498                    cbs = new ArrayList<Callback>();
499                    mPackageModeWatchers.put(packageName, cbs);
500                }
501                cbs.add(cb);
502            }
503        }
504    }
505
506    @Override
507    public void stopWatchingMode(IAppOpsCallback callback) {
508        synchronized (this) {
509            Callback cb = mModeWatchers.remove(callback.asBinder());
510            if (cb != null) {
511                cb.unlinkToDeath();
512                for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
513                    ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
514                    cbs.remove(cb);
515                    if (cbs.size() <= 0) {
516                        mOpModeWatchers.removeAt(i);
517                    }
518                }
519                for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
520                    ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
521                    cbs.remove(cb);
522                    if (cbs.size() <= 0) {
523                        mPackageModeWatchers.removeAt(i);
524                    }
525                }
526            }
527        }
528    }
529
530    @Override
531    public IBinder getToken(IBinder clientToken) {
532        synchronized (this) {
533            ClientState cs = mClients.get(clientToken);
534            if (cs == null) {
535                cs = new ClientState(clientToken);
536                mClients.put(clientToken, cs);
537            }
538            return cs;
539        }
540    }
541
542    @Override
543    public int checkOperation(int code, int uid, String packageName) {
544        verifyIncomingUid(uid);
545        verifyIncomingOp(code);
546        synchronized (this) {
547            Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
548            if (op == null) {
549                return AppOpsManager.opToDefaultMode(code);
550            }
551            return op.mode;
552        }
553    }
554
555    @Override
556    public int checkPackage(int uid, String packageName) {
557        synchronized (this) {
558            if (getOpsLocked(uid, packageName, true) != null) {
559                return AppOpsManager.MODE_ALLOWED;
560            } else {
561                return AppOpsManager.MODE_ERRORED;
562            }
563        }
564    }
565
566    @Override
567    public int noteOperation(int code, int uid, String packageName) {
568        verifyIncomingUid(uid);
569        verifyIncomingOp(code);
570        synchronized (this) {
571            Ops ops = getOpsLocked(uid, packageName, true);
572            if (ops == null) {
573                if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
574                        + " package " + packageName);
575                return AppOpsManager.MODE_ERRORED;
576            }
577            Op op = getOpLocked(ops, code, true);
578            if (op.duration == -1) {
579                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
580                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
581            }
582            op.duration = 0;
583            final int switchCode = AppOpsManager.opToSwitch(code);
584            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
585            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
586                if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
587                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
588                op.rejectTime = System.currentTimeMillis();
589                return switchOp.mode;
590            }
591            if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
592                    + " package " + packageName);
593            op.time = System.currentTimeMillis();
594            op.rejectTime = 0;
595            return AppOpsManager.MODE_ALLOWED;
596        }
597    }
598
599    @Override
600    public int startOperation(IBinder token, int code, int uid, String packageName) {
601        verifyIncomingUid(uid);
602        verifyIncomingOp(code);
603        ClientState client = (ClientState)token;
604        synchronized (this) {
605            Ops ops = getOpsLocked(uid, packageName, true);
606            if (ops == null) {
607                if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
608                        + " package " + packageName);
609                return AppOpsManager.MODE_ERRORED;
610            }
611            Op op = getOpLocked(ops, code, true);
612            final int switchCode = AppOpsManager.opToSwitch(code);
613            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
614            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
615                if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
616                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
617                op.rejectTime = System.currentTimeMillis();
618                return switchOp.mode;
619            }
620            if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
621                    + " package " + packageName);
622            if (op.nesting == 0) {
623                op.time = System.currentTimeMillis();
624                op.rejectTime = 0;
625                op.duration = -1;
626            }
627            op.nesting++;
628            if (client.mStartedOps != null) {
629                client.mStartedOps.add(op);
630            }
631            return AppOpsManager.MODE_ALLOWED;
632        }
633    }
634
635    @Override
636    public void finishOperation(IBinder token, int code, int uid, String packageName) {
637        verifyIncomingUid(uid);
638        verifyIncomingOp(code);
639        ClientState client = (ClientState)token;
640        synchronized (this) {
641            Op op = getOpLocked(code, uid, packageName, true);
642            if (op == null) {
643                return;
644            }
645            if (client.mStartedOps != null) {
646                if (!client.mStartedOps.remove(op)) {
647                    throw new IllegalStateException("Operation not started: uid" + op.uid
648                            + " pkg=" + op.packageName + " op=" + op.op);
649                }
650            }
651            finishOperationLocked(op);
652        }
653    }
654
655    void finishOperationLocked(Op op) {
656        if (op.nesting <= 1) {
657            if (op.nesting == 1) {
658                op.duration = (int)(System.currentTimeMillis() - op.time);
659                op.time += op.duration;
660            } else {
661                Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
662                        + op.packageName + " code " + op.op + " time=" + op.time
663                        + " duration=" + op.duration + " nesting=" + op.nesting);
664            }
665            op.nesting = 0;
666        } else {
667            op.nesting--;
668        }
669    }
670
671    private void verifyIncomingUid(int uid) {
672        if (uid == Binder.getCallingUid()) {
673            return;
674        }
675        if (Binder.getCallingPid() == Process.myPid()) {
676            return;
677        }
678        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
679                Binder.getCallingPid(), Binder.getCallingUid(), null);
680    }
681
682    private void verifyIncomingOp(int op) {
683        if (op >= 0 && op < AppOpsManager._NUM_OP) {
684            return;
685        }
686        throw new IllegalArgumentException("Bad operation #" + op);
687    }
688
689    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
690        HashMap<String, Ops> pkgOps = mUidOps.get(uid);
691        if (pkgOps == null) {
692            if (!edit) {
693                return null;
694            }
695            pkgOps = new HashMap<String, Ops>();
696            mUidOps.put(uid, pkgOps);
697        }
698        if (uid == 0) {
699            packageName = "root";
700        } else if (uid == Process.SHELL_UID) {
701            packageName = "com.android.shell";
702        }
703        Ops ops = pkgOps.get(packageName);
704        if (ops == null) {
705            if (!edit) {
706                return null;
707            }
708            // This is the first time we have seen this package name under this uid,
709            // so let's make sure it is valid.
710            if (uid != 0) {
711                final long ident = Binder.clearCallingIdentity();
712                try {
713                    int pkgUid = -1;
714                    try {
715                        pkgUid = mContext.getPackageManager().getPackageUid(packageName,
716                                UserHandle.getUserId(uid));
717                    } catch (NameNotFoundException e) {
718                        if ("media".equals(packageName)) {
719                            pkgUid = Process.MEDIA_UID;
720                        }
721                    }
722                    if (pkgUid != uid) {
723                        // Oops!  The package name is not valid for the uid they are calling
724                        // under.  Abort.
725                        Slog.w(TAG, "Bad call: specified package " + packageName
726                                + " under uid " + uid + " but it is really " + pkgUid);
727                        return null;
728                    }
729                } finally {
730                    Binder.restoreCallingIdentity(ident);
731                }
732            }
733            ops = new Ops(packageName, uid);
734            pkgOps.put(packageName, ops);
735        }
736        return ops;
737    }
738
739    private void scheduleWriteLocked() {
740        if (!mWriteScheduled) {
741            mWriteScheduled = true;
742            mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
743        }
744    }
745
746    private void scheduleWriteNowLocked() {
747        if (!mWriteScheduled) {
748            mWriteScheduled = true;
749        }
750        mHandler.removeCallbacks(mWriteRunner);
751        mHandler.post(mWriteRunner);
752    }
753
754    private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
755        Ops ops = getOpsLocked(uid, packageName, edit);
756        if (ops == null) {
757            return null;
758        }
759        return getOpLocked(ops, code, edit);
760    }
761
762    private Op getOpLocked(Ops ops, int code, boolean edit) {
763        Op op = ops.get(code);
764        if (op == null) {
765            if (!edit) {
766                return null;
767            }
768            op = new Op(ops.uid, ops.packageName, code);
769            ops.put(code, op);
770        }
771        if (edit) {
772            scheduleWriteLocked();
773        }
774        return op;
775    }
776
777    void readState() {
778        synchronized (mFile) {
779            synchronized (this) {
780                FileInputStream stream;
781                try {
782                    stream = mFile.openRead();
783                } catch (FileNotFoundException e) {
784                    Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
785                    return;
786                }
787                boolean success = false;
788                try {
789                    XmlPullParser parser = Xml.newPullParser();
790                    parser.setInput(stream, null);
791                    int type;
792                    while ((type = parser.next()) != XmlPullParser.START_TAG
793                            && type != XmlPullParser.END_DOCUMENT) {
794                        ;
795                    }
796
797                    if (type != XmlPullParser.START_TAG) {
798                        throw new IllegalStateException("no start tag found");
799                    }
800
801                    int outerDepth = parser.getDepth();
802                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
803                            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
804                        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
805                            continue;
806                        }
807
808                        String tagName = parser.getName();
809                        if (tagName.equals("pkg")) {
810                            readPackage(parser);
811                        } else {
812                            Slog.w(TAG, "Unknown element under <app-ops>: "
813                                    + parser.getName());
814                            XmlUtils.skipCurrentTag(parser);
815                        }
816                    }
817                    success = true;
818                } catch (IllegalStateException e) {
819                    Slog.w(TAG, "Failed parsing " + e);
820                } catch (NullPointerException e) {
821                    Slog.w(TAG, "Failed parsing " + e);
822                } catch (NumberFormatException e) {
823                    Slog.w(TAG, "Failed parsing " + e);
824                } catch (XmlPullParserException e) {
825                    Slog.w(TAG, "Failed parsing " + e);
826                } catch (IOException e) {
827                    Slog.w(TAG, "Failed parsing " + e);
828                } catch (IndexOutOfBoundsException e) {
829                    Slog.w(TAG, "Failed parsing " + e);
830                } finally {
831                    if (!success) {
832                        mUidOps.clear();
833                    }
834                    try {
835                        stream.close();
836                    } catch (IOException e) {
837                    }
838                }
839            }
840        }
841    }
842
843    void readPackage(XmlPullParser parser) throws NumberFormatException,
844            XmlPullParserException, IOException {
845        String pkgName = parser.getAttributeValue(null, "n");
846        int outerDepth = parser.getDepth();
847        int type;
848        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
849                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
850            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
851                continue;
852            }
853
854            String tagName = parser.getName();
855            if (tagName.equals("uid")) {
856                readUid(parser, pkgName);
857            } else {
858                Slog.w(TAG, "Unknown element under <pkg>: "
859                        + parser.getName());
860                XmlUtils.skipCurrentTag(parser);
861            }
862        }
863    }
864
865    void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
866            XmlPullParserException, IOException {
867        int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
868        int outerDepth = parser.getDepth();
869        int type;
870        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
871                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
872            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
873                continue;
874            }
875
876            String tagName = parser.getName();
877            if (tagName.equals("op")) {
878                Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
879                String mode = parser.getAttributeValue(null, "m");
880                if (mode != null) {
881                    op.mode = Integer.parseInt(mode);
882                }
883                String time = parser.getAttributeValue(null, "t");
884                if (time != null) {
885                    op.time = Long.parseLong(time);
886                }
887                time = parser.getAttributeValue(null, "r");
888                if (time != null) {
889                    op.rejectTime = Long.parseLong(time);
890                }
891                String dur = parser.getAttributeValue(null, "d");
892                if (dur != null) {
893                    op.duration = Integer.parseInt(dur);
894                }
895                HashMap<String, Ops> pkgOps = mUidOps.get(uid);
896                if (pkgOps == null) {
897                    pkgOps = new HashMap<String, Ops>();
898                    mUidOps.put(uid, pkgOps);
899                }
900                Ops ops = pkgOps.get(pkgName);
901                if (ops == null) {
902                    ops = new Ops(pkgName, uid);
903                    pkgOps.put(pkgName, ops);
904                }
905                ops.put(op.op, op);
906            } else {
907                Slog.w(TAG, "Unknown element under <pkg>: "
908                        + parser.getName());
909                XmlUtils.skipCurrentTag(parser);
910            }
911        }
912    }
913
914    void writeState() {
915        synchronized (mFile) {
916            List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
917
918            FileOutputStream stream;
919            try {
920                stream = mFile.startWrite();
921            } catch (IOException e) {
922                Slog.w(TAG, "Failed to write state: " + e);
923                return;
924            }
925
926            try {
927                XmlSerializer out = new FastXmlSerializer();
928                out.setOutput(stream, "utf-8");
929                out.startDocument(null, true);
930                out.startTag(null, "app-ops");
931
932                if (allOps != null) {
933                    String lastPkg = null;
934                    for (int i=0; i<allOps.size(); i++) {
935                        AppOpsManager.PackageOps pkg = allOps.get(i);
936                        if (!pkg.getPackageName().equals(lastPkg)) {
937                            if (lastPkg != null) {
938                                out.endTag(null, "pkg");
939                            }
940                            lastPkg = pkg.getPackageName();
941                            out.startTag(null, "pkg");
942                            out.attribute(null, "n", lastPkg);
943                        }
944                        out.startTag(null, "uid");
945                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
946                        List<AppOpsManager.OpEntry> ops = pkg.getOps();
947                        for (int j=0; j<ops.size(); j++) {
948                            AppOpsManager.OpEntry op = ops.get(j);
949                            out.startTag(null, "op");
950                            out.attribute(null, "n", Integer.toString(op.getOp()));
951                            if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
952                                out.attribute(null, "m", Integer.toString(op.getMode()));
953                            }
954                            long time = op.getTime();
955                            if (time != 0) {
956                                out.attribute(null, "t", Long.toString(time));
957                            }
958                            time = op.getRejectTime();
959                            if (time != 0) {
960                                out.attribute(null, "r", Long.toString(time));
961                            }
962                            int dur = op.getDuration();
963                            if (dur != 0) {
964                                out.attribute(null, "d", Integer.toString(dur));
965                            }
966                            out.endTag(null, "op");
967                        }
968                        out.endTag(null, "uid");
969                    }
970                    if (lastPkg != null) {
971                        out.endTag(null, "pkg");
972                    }
973                }
974
975                out.endTag(null, "app-ops");
976                out.endDocument();
977                mFile.finishWrite(stream);
978            } catch (IOException e) {
979                Slog.w(TAG, "Failed to write state, restoring backup.", e);
980                mFile.failWrite(stream);
981            }
982        }
983    }
984
985    @Override
986    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
987        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
988                != PackageManager.PERMISSION_GRANTED) {
989            pw.println("Permission Denial: can't dump ApOps service from from pid="
990                    + Binder.getCallingPid()
991                    + ", uid=" + Binder.getCallingUid());
992            return;
993        }
994
995        synchronized (this) {
996            pw.println("Current AppOps Service state:");
997            final long now = System.currentTimeMillis();
998            boolean needSep = false;
999            if (mOpModeWatchers.size() > 0) {
1000                needSep = true;
1001                pw.println("  Op mode watchers:");
1002                for (int i=0; i<mOpModeWatchers.size(); i++) {
1003                    pw.print("    Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
1004                    pw.println(":");
1005                    ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
1006                    for (int j=0; j<callbacks.size(); j++) {
1007                        pw.print("      #"); pw.print(j); pw.print(": ");
1008                        pw.println(callbacks.get(j));
1009                    }
1010                }
1011            }
1012            if (mPackageModeWatchers.size() > 0) {
1013                needSep = true;
1014                pw.println("  Package mode watchers:");
1015                for (int i=0; i<mPackageModeWatchers.size(); i++) {
1016                    pw.print("    Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
1017                    pw.println(":");
1018                    ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
1019                    for (int j=0; j<callbacks.size(); j++) {
1020                        pw.print("      #"); pw.print(j); pw.print(": ");
1021                        pw.println(callbacks.get(j));
1022                    }
1023                }
1024            }
1025            if (mModeWatchers.size() > 0) {
1026                needSep = true;
1027                pw.println("  All mode watchers:");
1028                for (int i=0; i<mModeWatchers.size(); i++) {
1029                    pw.print("    "); pw.print(mModeWatchers.keyAt(i));
1030                    pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
1031                }
1032            }
1033            if (mClients.size() > 0) {
1034                needSep = true;
1035                pw.println("  Clients:");
1036                for (int i=0; i<mClients.size(); i++) {
1037                    pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
1038                    ClientState cs = mClients.valueAt(i);
1039                    pw.print("      "); pw.println(cs);
1040                    if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
1041                        pw.println("      Started ops:");
1042                        for (int j=0; j<cs.mStartedOps.size(); j++) {
1043                            Op op = cs.mStartedOps.get(j);
1044                            pw.print("        "); pw.print("uid="); pw.print(op.uid);
1045                            pw.print(" pkg="); pw.print(op.packageName);
1046                            pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
1047                        }
1048                    }
1049                }
1050            }
1051            if (needSep) {
1052                pw.println();
1053            }
1054            for (int i=0; i<mUidOps.size(); i++) {
1055                pw.print("  Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
1056                HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
1057                for (Ops ops : pkgOps.values()) {
1058                    pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
1059                    for (int j=0; j<ops.size(); j++) {
1060                        Op op = ops.valueAt(j);
1061                        pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
1062                        pw.print(": mode="); pw.print(op.mode);
1063                        if (op.time != 0) {
1064                            pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
1065                            pw.print(" ago");
1066                        }
1067                        if (op.rejectTime != 0) {
1068                            pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
1069                            pw.print(" ago");
1070                        }
1071                        if (op.duration == -1) {
1072                            pw.println(" (running)");
1073                        } else {
1074                            pw.print("; duration=");
1075                                    TimeUtils.formatDuration(op.duration, pw);
1076                                    pw.println();
1077                        }
1078                    }
1079                }
1080            }
1081        }
1082    }
1083}
1084