AppOpsService.java revision f584f014dbb281727fccfea10bc9c2539a752f17
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        verifyIncomingUid(uid);
372        verifyIncomingOp(code);
373        ArrayList<Callback> repCbs = null;
374        code = AppOpsManager.opToSwitch(code);
375        synchronized (this) {
376            Op op = getOpLocked(code, uid, packageName, true);
377            if (op != null) {
378                if (op.mode != mode) {
379                    op.mode = mode;
380                    ArrayList<Callback> cbs = mOpModeWatchers.get(code);
381                    if (cbs != null) {
382                        if (repCbs == null) {
383                            repCbs = new ArrayList<Callback>();
384                        }
385                        repCbs.addAll(cbs);
386                    }
387                    cbs = mPackageModeWatchers.get(packageName);
388                    if (cbs != null) {
389                        if (repCbs == null) {
390                            repCbs = new ArrayList<Callback>();
391                        }
392                        repCbs.addAll(cbs);
393                    }
394                    if (mode == AppOpsManager.opToDefaultMode(op.op)) {
395                        // If going into the default mode, prune this op
396                        // if there is nothing else interesting in it.
397                        pruneOp(op, uid, packageName);
398                    }
399                    scheduleWriteNowLocked();
400                }
401            }
402        }
403        if (repCbs != null) {
404            for (int i=0; i<repCbs.size(); i++) {
405                try {
406                    repCbs.get(i).mCallback.opChanged(code, packageName);
407                } catch (RemoteException e) {
408                }
409            }
410        }
411    }
412
413    private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
414            HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
415            String packageName, int op, ArrayList<Callback> cbs) {
416        if (cbs == null) {
417            return callbacks;
418        }
419        if (callbacks == null) {
420            callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
421        }
422        for (int i=0; i<cbs.size(); i++) {
423            Callback cb = cbs.get(i);
424            ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
425            if (reports == null) {
426                reports = new ArrayList<Pair<String, Integer>>();
427                callbacks.put(cb, reports);
428            }
429            reports.add(new Pair<String, Integer>(packageName, op));
430        }
431        return callbacks;
432    }
433
434    @Override
435    public void resetAllModes() {
436        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
437                Binder.getCallingPid(), Binder.getCallingUid(), null);
438        HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
439        synchronized (this) {
440            boolean changed = false;
441            for (int i=mUidOps.size()-1; i>=0; i--) {
442                HashMap<String, Ops> packages = mUidOps.valueAt(i);
443                Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
444                while (it.hasNext()) {
445                    Map.Entry<String, Ops> ent = it.next();
446                    String packageName = ent.getKey();
447                    Ops pkgOps = ent.getValue();
448                    for (int j=pkgOps.size()-1; j>=0; j--) {
449                        Op curOp = pkgOps.valueAt(j);
450                        if (AppOpsManager.opAllowsReset(curOp.op)
451                                && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
452                            curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
453                            changed = true;
454                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
455                                    mOpModeWatchers.get(curOp.op));
456                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
457                                    mPackageModeWatchers.get(packageName));
458                            if (curOp.time == 0 && curOp.rejectTime == 0) {
459                                pkgOps.removeAt(j);
460                            }
461                        }
462                    }
463                    if (pkgOps.size() == 0) {
464                        it.remove();
465                    }
466                }
467                if (packages.size() == 0) {
468                    mUidOps.removeAt(i);
469                }
470            }
471            if (changed) {
472                scheduleWriteNowLocked();
473            }
474        }
475        if (callbacks != null) {
476            for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
477                Callback cb = ent.getKey();
478                ArrayList<Pair<String, Integer>> reports = ent.getValue();
479                for (int i=0; i<reports.size(); i++) {
480                    Pair<String, Integer> rep = reports.get(i);
481                    try {
482                        cb.mCallback.opChanged(rep.second, rep.first);
483                    } catch (RemoteException e) {
484                    }
485                }
486            }
487        }
488    }
489
490    @Override
491    public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
492        synchronized (this) {
493            op = AppOpsManager.opToSwitch(op);
494            Callback cb = mModeWatchers.get(callback.asBinder());
495            if (cb == null) {
496                cb = new Callback(callback);
497                mModeWatchers.put(callback.asBinder(), cb);
498            }
499            if (op != AppOpsManager.OP_NONE) {
500                ArrayList<Callback> cbs = mOpModeWatchers.get(op);
501                if (cbs == null) {
502                    cbs = new ArrayList<Callback>();
503                    mOpModeWatchers.put(op, cbs);
504                }
505                cbs.add(cb);
506            }
507            if (packageName != null) {
508                ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
509                if (cbs == null) {
510                    cbs = new ArrayList<Callback>();
511                    mPackageModeWatchers.put(packageName, cbs);
512                }
513                cbs.add(cb);
514            }
515        }
516    }
517
518    @Override
519    public void stopWatchingMode(IAppOpsCallback callback) {
520        synchronized (this) {
521            Callback cb = mModeWatchers.remove(callback.asBinder());
522            if (cb != null) {
523                cb.unlinkToDeath();
524                for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
525                    ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
526                    cbs.remove(cb);
527                    if (cbs.size() <= 0) {
528                        mOpModeWatchers.removeAt(i);
529                    }
530                }
531                for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
532                    ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
533                    cbs.remove(cb);
534                    if (cbs.size() <= 0) {
535                        mPackageModeWatchers.removeAt(i);
536                    }
537                }
538            }
539        }
540    }
541
542    @Override
543    public IBinder getToken(IBinder clientToken) {
544        synchronized (this) {
545            ClientState cs = mClients.get(clientToken);
546            if (cs == null) {
547                cs = new ClientState(clientToken);
548                mClients.put(clientToken, cs);
549            }
550            return cs;
551        }
552    }
553
554    @Override
555    public int checkOperation(int code, int uid, String packageName) {
556        verifyIncomingUid(uid);
557        verifyIncomingOp(code);
558        synchronized (this) {
559            if (isOpRestricted(uid, code)) {
560                return AppOpsManager.MODE_IGNORED;
561            }
562            Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
563            if (op == null) {
564                return AppOpsManager.opToDefaultMode(code);
565            }
566            return op.mode;
567        }
568    }
569
570    @Override
571    public int checkAudioOperation(int code, int stream, int uid, String packageName) {
572        synchronized (this) {
573            final int mode = checkRestrictionLocked(code, stream, uid, packageName);
574            if (mode != AppOpsManager.MODE_ALLOWED) {
575                return mode;
576            }
577        }
578        return checkOperation(code, uid, packageName);
579    }
580
581    private int checkRestrictionLocked(int code, int stream, int uid, String packageName) {
582        final SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
583        if (streamRestrictions != null) {
584            final Restriction r = streamRestrictions.get(stream);
585            if (r != null && !r.exceptionPackages.contains(packageName)) {
586                return r.mode;
587            }
588        }
589        return AppOpsManager.MODE_ALLOWED;
590    }
591
592    @Override
593    public void setAudioRestriction(int code, int stream, int uid, int mode,
594            String[] exceptionPackages) {
595        verifyIncomingUid(uid);
596        verifyIncomingOp(code);
597        synchronized (this) {
598            SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
599            if (streamRestrictions == null) {
600                streamRestrictions = new SparseArray<Restriction>();
601                mAudioRestrictions.put(code, streamRestrictions);
602            }
603            streamRestrictions.remove(stream);
604            if (mode != AppOpsManager.MODE_ALLOWED) {
605                final Restriction r = new Restriction();
606                r.mode = mode;
607                if (exceptionPackages != null) {
608                    final int N = exceptionPackages.length;
609                    r.exceptionPackages = new ArraySet<String>(N);
610                    for (int i = 0; i < N; i++) {
611                        final String pkg = exceptionPackages[i];
612                        if (pkg != null) {
613                            r.exceptionPackages.add(pkg.trim());
614                        }
615                    }
616                }
617                streamRestrictions.put(stream, r);
618            }
619        }
620    }
621
622    @Override
623    public int checkPackage(int uid, String packageName) {
624        synchronized (this) {
625            if (getOpsLocked(uid, packageName, true) != null) {
626                return AppOpsManager.MODE_ALLOWED;
627            } else {
628                return AppOpsManager.MODE_ERRORED;
629            }
630        }
631    }
632
633    @Override
634    public int noteOperation(int code, int uid, String packageName) {
635        verifyIncomingUid(uid);
636        verifyIncomingOp(code);
637        synchronized (this) {
638            Ops ops = getOpsLocked(uid, packageName, true);
639            if (ops == null) {
640                if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
641                        + " package " + packageName);
642                return AppOpsManager.MODE_ERRORED;
643            }
644            Op op = getOpLocked(ops, code, true);
645            if (isOpRestricted(uid, code)) {
646                return AppOpsManager.MODE_IGNORED;
647            }
648            if (op.duration == -1) {
649                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
650                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
651            }
652            op.duration = 0;
653            final int switchCode = AppOpsManager.opToSwitch(code);
654            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
655            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
656                if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
657                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
658                op.rejectTime = System.currentTimeMillis();
659                return switchOp.mode;
660            }
661            if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
662                    + " package " + packageName);
663            op.time = System.currentTimeMillis();
664            op.rejectTime = 0;
665            return AppOpsManager.MODE_ALLOWED;
666        }
667    }
668
669    @Override
670    public int startOperation(IBinder token, int code, int uid, String packageName) {
671        verifyIncomingUid(uid);
672        verifyIncomingOp(code);
673        ClientState client = (ClientState)token;
674        synchronized (this) {
675            Ops ops = getOpsLocked(uid, packageName, true);
676            if (ops == null) {
677                if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
678                        + " package " + packageName);
679                return AppOpsManager.MODE_ERRORED;
680            }
681            Op op = getOpLocked(ops, code, true);
682            if (isOpRestricted(uid, code)) {
683                return AppOpsManager.MODE_IGNORED;
684            }
685            final int switchCode = AppOpsManager.opToSwitch(code);
686            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
687            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
688                if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
689                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
690                op.rejectTime = System.currentTimeMillis();
691                return switchOp.mode;
692            }
693            if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
694                    + " package " + packageName);
695            if (op.nesting == 0) {
696                op.time = System.currentTimeMillis();
697                op.rejectTime = 0;
698                op.duration = -1;
699            }
700            op.nesting++;
701            if (client.mStartedOps != null) {
702                client.mStartedOps.add(op);
703            }
704            return AppOpsManager.MODE_ALLOWED;
705        }
706    }
707
708    @Override
709    public void finishOperation(IBinder token, int code, int uid, String packageName) {
710        verifyIncomingUid(uid);
711        verifyIncomingOp(code);
712        ClientState client = (ClientState)token;
713        synchronized (this) {
714            Op op = getOpLocked(code, uid, packageName, true);
715            if (op == null) {
716                return;
717            }
718            if (client.mStartedOps != null) {
719                if (!client.mStartedOps.remove(op)) {
720                    throw new IllegalStateException("Operation not started: uid" + op.uid
721                            + " pkg=" + op.packageName + " op=" + op.op);
722                }
723            }
724            finishOperationLocked(op);
725        }
726    }
727
728    void finishOperationLocked(Op op) {
729        if (op.nesting <= 1) {
730            if (op.nesting == 1) {
731                op.duration = (int)(System.currentTimeMillis() - op.time);
732                op.time += op.duration;
733            } else {
734                Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
735                        + op.packageName + " code " + op.op + " time=" + op.time
736                        + " duration=" + op.duration + " nesting=" + op.nesting);
737            }
738            op.nesting = 0;
739        } else {
740            op.nesting--;
741        }
742    }
743
744    private void verifyIncomingUid(int uid) {
745        if (uid == Binder.getCallingUid()) {
746            return;
747        }
748        if (Binder.getCallingPid() == Process.myPid()) {
749            return;
750        }
751        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
752                Binder.getCallingPid(), Binder.getCallingUid(), null);
753    }
754
755    private void verifyIncomingOp(int op) {
756        if (op >= 0 && op < AppOpsManager._NUM_OP) {
757            return;
758        }
759        throw new IllegalArgumentException("Bad operation #" + op);
760    }
761
762    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
763        HashMap<String, Ops> pkgOps = mUidOps.get(uid);
764        if (pkgOps == null) {
765            if (!edit) {
766                return null;
767            }
768            pkgOps = new HashMap<String, Ops>();
769            mUidOps.put(uid, pkgOps);
770        }
771        if (uid == 0) {
772            packageName = "root";
773        } else if (uid == Process.SHELL_UID) {
774            packageName = "com.android.shell";
775        }
776        Ops ops = pkgOps.get(packageName);
777        if (ops == null) {
778            if (!edit) {
779                return null;
780            }
781            // This is the first time we have seen this package name under this uid,
782            // so let's make sure it is valid.
783            if (uid != 0) {
784                final long ident = Binder.clearCallingIdentity();
785                try {
786                    int pkgUid = -1;
787                    try {
788                        pkgUid = mContext.getPackageManager().getPackageUid(packageName,
789                                UserHandle.getUserId(uid));
790                    } catch (NameNotFoundException e) {
791                        if ("media".equals(packageName)) {
792                            pkgUid = Process.MEDIA_UID;
793                        }
794                    }
795                    if (pkgUid != uid) {
796                        // Oops!  The package name is not valid for the uid they are calling
797                        // under.  Abort.
798                        Slog.w(TAG, "Bad call: specified package " + packageName
799                                + " under uid " + uid + " but it is really " + pkgUid);
800                        return null;
801                    }
802                } finally {
803                    Binder.restoreCallingIdentity(ident);
804                }
805            }
806            ops = new Ops(packageName, uid);
807            pkgOps.put(packageName, ops);
808        }
809        return ops;
810    }
811
812    private void scheduleWriteLocked() {
813        if (!mWriteScheduled) {
814            mWriteScheduled = true;
815            mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
816        }
817    }
818
819    private void scheduleWriteNowLocked() {
820        if (!mWriteScheduled) {
821            mWriteScheduled = true;
822        }
823        mHandler.removeCallbacks(mWriteRunner);
824        mHandler.post(mWriteRunner);
825    }
826
827    private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
828        Ops ops = getOpsLocked(uid, packageName, edit);
829        if (ops == null) {
830            return null;
831        }
832        return getOpLocked(ops, code, edit);
833    }
834
835    private Op getOpLocked(Ops ops, int code, boolean edit) {
836        Op op = ops.get(code);
837        if (op == null) {
838            if (!edit) {
839                return null;
840            }
841            op = new Op(ops.uid, ops.packageName, code);
842            ops.put(code, op);
843        }
844        if (edit) {
845            scheduleWriteLocked();
846        }
847        return op;
848    }
849
850    private boolean isOpRestricted(int uid, int code) {
851        int userHandle = UserHandle.getUserId(uid);
852        boolean[] opRestrictions = mOpRestrictions.get(userHandle);
853        if ((opRestrictions != null) && opRestrictions[code]) {
854            if (userHandle == UserHandle.USER_OWNER) {
855                if (uid != mDeviceOwnerUid) {
856                    return true;
857                }
858            } else {
859                if (uid != mProfileOwnerUids.get(userHandle, -1)) {
860                    return true;
861                }
862            }
863        }
864        return false;
865    }
866
867    void readState() {
868        synchronized (mFile) {
869            synchronized (this) {
870                FileInputStream stream;
871                try {
872                    stream = mFile.openRead();
873                } catch (FileNotFoundException e) {
874                    Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
875                    return;
876                }
877                boolean success = false;
878                try {
879                    XmlPullParser parser = Xml.newPullParser();
880                    parser.setInput(stream, null);
881                    int type;
882                    while ((type = parser.next()) != XmlPullParser.START_TAG
883                            && type != XmlPullParser.END_DOCUMENT) {
884                        ;
885                    }
886
887                    if (type != XmlPullParser.START_TAG) {
888                        throw new IllegalStateException("no start tag found");
889                    }
890
891                    int outerDepth = parser.getDepth();
892                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
893                            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
894                        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
895                            continue;
896                        }
897
898                        String tagName = parser.getName();
899                        if (tagName.equals("pkg")) {
900                            readPackage(parser);
901                        } else {
902                            Slog.w(TAG, "Unknown element under <app-ops>: "
903                                    + parser.getName());
904                            XmlUtils.skipCurrentTag(parser);
905                        }
906                    }
907                    success = true;
908                } catch (IllegalStateException e) {
909                    Slog.w(TAG, "Failed parsing " + e);
910                } catch (NullPointerException e) {
911                    Slog.w(TAG, "Failed parsing " + e);
912                } catch (NumberFormatException e) {
913                    Slog.w(TAG, "Failed parsing " + e);
914                } catch (XmlPullParserException e) {
915                    Slog.w(TAG, "Failed parsing " + e);
916                } catch (IOException e) {
917                    Slog.w(TAG, "Failed parsing " + e);
918                } catch (IndexOutOfBoundsException e) {
919                    Slog.w(TAG, "Failed parsing " + e);
920                } finally {
921                    if (!success) {
922                        mUidOps.clear();
923                    }
924                    try {
925                        stream.close();
926                    } catch (IOException e) {
927                    }
928                }
929            }
930        }
931    }
932
933    void readPackage(XmlPullParser parser) throws NumberFormatException,
934            XmlPullParserException, IOException {
935        String pkgName = parser.getAttributeValue(null, "n");
936        int outerDepth = parser.getDepth();
937        int type;
938        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
939                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
940            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
941                continue;
942            }
943
944            String tagName = parser.getName();
945            if (tagName.equals("uid")) {
946                readUid(parser, pkgName);
947            } else {
948                Slog.w(TAG, "Unknown element under <pkg>: "
949                        + parser.getName());
950                XmlUtils.skipCurrentTag(parser);
951            }
952        }
953    }
954
955    void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
956            XmlPullParserException, IOException {
957        int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
958        int outerDepth = parser.getDepth();
959        int type;
960        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
961                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
962            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
963                continue;
964            }
965
966            String tagName = parser.getName();
967            if (tagName.equals("op")) {
968                Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
969                String mode = parser.getAttributeValue(null, "m");
970                if (mode != null) {
971                    op.mode = Integer.parseInt(mode);
972                }
973                String time = parser.getAttributeValue(null, "t");
974                if (time != null) {
975                    op.time = Long.parseLong(time);
976                }
977                time = parser.getAttributeValue(null, "r");
978                if (time != null) {
979                    op.rejectTime = Long.parseLong(time);
980                }
981                String dur = parser.getAttributeValue(null, "d");
982                if (dur != null) {
983                    op.duration = Integer.parseInt(dur);
984                }
985                HashMap<String, Ops> pkgOps = mUidOps.get(uid);
986                if (pkgOps == null) {
987                    pkgOps = new HashMap<String, Ops>();
988                    mUidOps.put(uid, pkgOps);
989                }
990                Ops ops = pkgOps.get(pkgName);
991                if (ops == null) {
992                    ops = new Ops(pkgName, uid);
993                    pkgOps.put(pkgName, ops);
994                }
995                ops.put(op.op, op);
996            } else {
997                Slog.w(TAG, "Unknown element under <pkg>: "
998                        + parser.getName());
999                XmlUtils.skipCurrentTag(parser);
1000            }
1001        }
1002    }
1003
1004    void writeState() {
1005        synchronized (mFile) {
1006            List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
1007
1008            FileOutputStream stream;
1009            try {
1010                stream = mFile.startWrite();
1011            } catch (IOException e) {
1012                Slog.w(TAG, "Failed to write state: " + e);
1013                return;
1014            }
1015
1016            try {
1017                XmlSerializer out = new FastXmlSerializer();
1018                out.setOutput(stream, "utf-8");
1019                out.startDocument(null, true);
1020                out.startTag(null, "app-ops");
1021
1022                if (allOps != null) {
1023                    String lastPkg = null;
1024                    for (int i=0; i<allOps.size(); i++) {
1025                        AppOpsManager.PackageOps pkg = allOps.get(i);
1026                        if (!pkg.getPackageName().equals(lastPkg)) {
1027                            if (lastPkg != null) {
1028                                out.endTag(null, "pkg");
1029                            }
1030                            lastPkg = pkg.getPackageName();
1031                            out.startTag(null, "pkg");
1032                            out.attribute(null, "n", lastPkg);
1033                        }
1034                        out.startTag(null, "uid");
1035                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
1036                        List<AppOpsManager.OpEntry> ops = pkg.getOps();
1037                        for (int j=0; j<ops.size(); j++) {
1038                            AppOpsManager.OpEntry op = ops.get(j);
1039                            out.startTag(null, "op");
1040                            out.attribute(null, "n", Integer.toString(op.getOp()));
1041                            if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
1042                                out.attribute(null, "m", Integer.toString(op.getMode()));
1043                            }
1044                            long time = op.getTime();
1045                            if (time != 0) {
1046                                out.attribute(null, "t", Long.toString(time));
1047                            }
1048                            time = op.getRejectTime();
1049                            if (time != 0) {
1050                                out.attribute(null, "r", Long.toString(time));
1051                            }
1052                            int dur = op.getDuration();
1053                            if (dur != 0) {
1054                                out.attribute(null, "d", Integer.toString(dur));
1055                            }
1056                            out.endTag(null, "op");
1057                        }
1058                        out.endTag(null, "uid");
1059                    }
1060                    if (lastPkg != null) {
1061                        out.endTag(null, "pkg");
1062                    }
1063                }
1064
1065                out.endTag(null, "app-ops");
1066                out.endDocument();
1067                mFile.finishWrite(stream);
1068            } catch (IOException e) {
1069                Slog.w(TAG, "Failed to write state, restoring backup.", e);
1070                mFile.failWrite(stream);
1071            }
1072        }
1073    }
1074
1075    @Override
1076    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1077        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1078                != PackageManager.PERMISSION_GRANTED) {
1079            pw.println("Permission Denial: can't dump ApOps service from from pid="
1080                    + Binder.getCallingPid()
1081                    + ", uid=" + Binder.getCallingUid());
1082            return;
1083        }
1084
1085        synchronized (this) {
1086            pw.println("Current AppOps Service state:");
1087            final long now = System.currentTimeMillis();
1088            boolean needSep = false;
1089            if (mOpModeWatchers.size() > 0) {
1090                needSep = true;
1091                pw.println("  Op mode watchers:");
1092                for (int i=0; i<mOpModeWatchers.size(); i++) {
1093                    pw.print("    Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
1094                    pw.println(":");
1095                    ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
1096                    for (int j=0; j<callbacks.size(); j++) {
1097                        pw.print("      #"); pw.print(j); pw.print(": ");
1098                        pw.println(callbacks.get(j));
1099                    }
1100                }
1101            }
1102            if (mPackageModeWatchers.size() > 0) {
1103                needSep = true;
1104                pw.println("  Package mode watchers:");
1105                for (int i=0; i<mPackageModeWatchers.size(); i++) {
1106                    pw.print("    Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
1107                    pw.println(":");
1108                    ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
1109                    for (int j=0; j<callbacks.size(); j++) {
1110                        pw.print("      #"); pw.print(j); pw.print(": ");
1111                        pw.println(callbacks.get(j));
1112                    }
1113                }
1114            }
1115            if (mModeWatchers.size() > 0) {
1116                needSep = true;
1117                pw.println("  All mode watchers:");
1118                for (int i=0; i<mModeWatchers.size(); i++) {
1119                    pw.print("    "); pw.print(mModeWatchers.keyAt(i));
1120                    pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
1121                }
1122            }
1123            if (mClients.size() > 0) {
1124                needSep = true;
1125                pw.println("  Clients:");
1126                for (int i=0; i<mClients.size(); i++) {
1127                    pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
1128                    ClientState cs = mClients.valueAt(i);
1129                    pw.print("      "); pw.println(cs);
1130                    if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
1131                        pw.println("      Started ops:");
1132                        for (int j=0; j<cs.mStartedOps.size(); j++) {
1133                            Op op = cs.mStartedOps.get(j);
1134                            pw.print("        "); pw.print("uid="); pw.print(op.uid);
1135                            pw.print(" pkg="); pw.print(op.packageName);
1136                            pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
1137                        }
1138                    }
1139                }
1140            }
1141            if (mAudioRestrictions.size() > 0) {
1142                boolean printedHeader = false;
1143                for (int o=0; o<mAudioRestrictions.size(); o++) {
1144                    final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
1145                    final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o);
1146                    for (int i=0; i<restrictions.size(); i++) {
1147                        if (!printedHeader){
1148                            pw.println("  Audio Restrictions:");
1149                            printedHeader = true;
1150                            needSep = true;
1151                        }
1152                        final int stream = restrictions.keyAt(i);
1153                        pw.print("    "); pw.print(op);
1154                        pw.print(" stream="); pw.print(AudioService.streamToString(stream));
1155                        Restriction r = restrictions.valueAt(i);
1156                        pw.print(": mode="); pw.println(r.mode);
1157                        if (!r.exceptionPackages.isEmpty()) {
1158                            pw.println("      Exceptions:");
1159                            for (int j=0; j<r.exceptionPackages.size(); j++) {
1160                                pw.print("        "); pw.println(r.exceptionPackages.valueAt(j));
1161                            }
1162                        }
1163                    }
1164                }
1165            }
1166            if (needSep) {
1167                pw.println();
1168            }
1169            for (int i=0; i<mUidOps.size(); i++) {
1170                pw.print("  Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
1171                HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
1172                for (Ops ops : pkgOps.values()) {
1173                    pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
1174                    for (int j=0; j<ops.size(); j++) {
1175                        Op op = ops.valueAt(j);
1176                        pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
1177                        pw.print(": mode="); pw.print(op.mode);
1178                        if (op.time != 0) {
1179                            pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
1180                            pw.print(" ago");
1181                        }
1182                        if (op.rejectTime != 0) {
1183                            pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
1184                            pw.print(" ago");
1185                        }
1186                        if (op.duration == -1) {
1187                            pw.println(" (running)");
1188                        } else {
1189                            pw.print("; duration=");
1190                                    TimeUtils.formatDuration(op.duration, pw);
1191                                    pw.println();
1192                        }
1193                    }
1194                }
1195            }
1196        }
1197    }
1198
1199    private static final class Restriction {
1200        private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
1201        int mode;
1202        ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
1203    }
1204
1205    @Override
1206    public void setDeviceOwner(String packageName) throws RemoteException {
1207        checkSystemUid("setDeviceOwner");
1208        try {
1209            mDeviceOwnerUid = mContext.getPackageManager().getPackageUid(packageName,
1210                    UserHandle.USER_OWNER);
1211        } catch (NameNotFoundException e) {
1212            Log.e(TAG, "Could not find Device Owner UID");
1213            mDeviceOwnerUid = -1;
1214            throw new IllegalArgumentException("Could not find device owner package "
1215                    + packageName);
1216        }
1217    }
1218
1219    @Override
1220    public void setProfileOwner(String packageName, int userHandle) throws RemoteException {
1221        checkSystemUid("setProfileOwner");
1222        try {
1223            int uid = mContext.getPackageManager().getPackageUid(packageName,
1224                    userHandle);
1225            mProfileOwnerUids.put(userHandle, uid);
1226        } catch (NameNotFoundException e) {
1227            Log.e(TAG, "Could not find Profile Owner UID");
1228            mProfileOwnerUids.put(userHandle, -1);
1229            throw new IllegalArgumentException("Could not find profile owner package "
1230                    + packageName);
1231        }
1232    }
1233
1234    @Override
1235    public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException {
1236        checkSystemUid("setUserRestrictions");
1237        boolean[] opRestrictions = mOpRestrictions.get(userHandle);
1238        if (opRestrictions == null) {
1239            opRestrictions = new boolean[AppOpsManager._NUM_OP];
1240            mOpRestrictions.put(userHandle, opRestrictions);
1241        }
1242        for (int i = 0; i < opRestrictions.length; ++i) {
1243            String restriction = AppOpsManager.opToRestriction(i);
1244            if (restriction != null) {
1245                opRestrictions[i] = restrictions.getBoolean(restriction, false);
1246            } else {
1247                opRestrictions[i] = false;
1248            }
1249        }
1250    }
1251
1252    @Override
1253    public void removeUser(int userHandle) throws RemoteException {
1254        checkSystemUid("removeUser");
1255        mOpRestrictions.remove(userHandle);
1256        final int index = mProfileOwnerUids.indexOfKey(userHandle);
1257        if (index >= 0) {
1258            mProfileOwnerUids.removeAt(index);
1259        }
1260    }
1261
1262    private void checkSystemUid(String function) {
1263        int uid = Binder.getCallingUid();
1264        if (uid != Process.SYSTEM_UID) {
1265            throw new SecurityException(function + " must by called by the system");
1266        }
1267    }
1268
1269}
1270