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