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.nio.charset.StandardCharsets;
27import java.util.ArrayList;
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.Iterator;
31import java.util.List;
32import java.util.Map;
33
34import android.app.ActivityManager;
35import android.app.ActivityThread;
36import android.app.AppGlobals;
37import android.app.AppOpsManager;
38import android.content.Context;
39import android.content.pm.ApplicationInfo;
40import android.content.pm.IPackageManager;
41import android.content.pm.PackageManager;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.media.AudioAttributes;
44import android.os.AsyncTask;
45import android.os.Binder;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.IBinder;
49import android.os.Process;
50import android.os.RemoteException;
51import android.os.ServiceManager;
52import android.os.UserHandle;
53import android.os.storage.MountServiceInternal;
54import android.util.ArrayMap;
55import android.util.ArraySet;
56import android.util.AtomicFile;
57import android.util.Log;
58import android.util.Pair;
59import android.util.Slog;
60import android.util.SparseArray;
61import android.util.SparseIntArray;
62import android.util.TimeUtils;
63import android.util.Xml;
64
65import com.android.internal.app.IAppOpsService;
66import com.android.internal.app.IAppOpsCallback;
67import com.android.internal.os.Zygote;
68import com.android.internal.util.ArrayUtils;
69import com.android.internal.util.FastXmlSerializer;
70import com.android.internal.util.XmlUtils;
71
72import libcore.util.EmptyArray;
73import org.xmlpull.v1.XmlPullParser;
74import org.xmlpull.v1.XmlPullParserException;
75import org.xmlpull.v1.XmlSerializer;
76
77public class AppOpsService extends IAppOpsService.Stub {
78    static final String TAG = "AppOps";
79    static final boolean DEBUG = false;
80
81    // Write at most every 30 minutes.
82    static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
83
84    Context mContext;
85    final AtomicFile mFile;
86    final Handler mHandler;
87
88    boolean mWriteScheduled;
89    boolean mFastWriteScheduled;
90    final Runnable mWriteRunner = new Runnable() {
91        public void run() {
92            synchronized (AppOpsService.this) {
93                mWriteScheduled = false;
94                mFastWriteScheduled = false;
95                AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
96                    @Override protected Void doInBackground(Void... params) {
97                        writeState();
98                        return null;
99                    }
100                };
101                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
102            }
103        }
104    };
105
106    final SparseArray<UidState> mUidStates = new SparseArray<>();
107
108    private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>();
109
110    private static final class UidState {
111        public final int uid;
112        public ArrayMap<String, Ops> pkgOps;
113        public SparseIntArray opModes;
114
115        public UidState(int uid) {
116            this.uid = uid;
117        }
118
119        public void clear() {
120            pkgOps = null;
121            opModes = null;
122        }
123
124        public boolean isDefault() {
125            return (pkgOps == null || pkgOps.isEmpty())
126                    && (opModes == null || opModes.size() <= 0);
127        }
128    }
129
130    public final static class Ops extends SparseArray<Op> {
131        public final String packageName;
132        public final UidState uidState;
133        public final boolean isPrivileged;
134
135        public Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
136            packageName = _packageName;
137            uidState = _uidState;
138            isPrivileged = _isPrivileged;
139        }
140    }
141
142    public final static class Op {
143        public final int uid;
144        public final String packageName;
145        public int proxyUid = -1;
146        public String proxyPackageName;
147        public final int op;
148        public int mode;
149        public int duration;
150        public long time;
151        public long rejectTime;
152        public int nesting;
153
154        public Op(int _uid, String _packageName, int _op) {
155            uid = _uid;
156            packageName = _packageName;
157            op = _op;
158            mode = AppOpsManager.opToDefaultMode(op);
159        }
160    }
161
162    final SparseArray<ArrayList<Callback>> mOpModeWatchers
163            = new SparseArray<ArrayList<Callback>>();
164    final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers
165            = new ArrayMap<String, ArrayList<Callback>>();
166    final ArrayMap<IBinder, Callback> mModeWatchers
167            = new ArrayMap<IBinder, Callback>();
168    final SparseArray<SparseArray<Restriction>> mAudioRestrictions
169            = new SparseArray<SparseArray<Restriction>>();
170
171    public final class Callback implements DeathRecipient {
172        final IAppOpsCallback mCallback;
173
174        public Callback(IAppOpsCallback callback) {
175            mCallback = callback;
176            try {
177                mCallback.asBinder().linkToDeath(this, 0);
178            } catch (RemoteException e) {
179            }
180        }
181
182        public void unlinkToDeath() {
183            mCallback.asBinder().unlinkToDeath(this, 0);
184        }
185
186        @Override
187        public void binderDied() {
188            stopWatchingMode(mCallback);
189        }
190    }
191
192    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
193
194    public final class ClientState extends Binder implements DeathRecipient {
195        final IBinder mAppToken;
196        final int mPid;
197        final ArrayList<Op> mStartedOps;
198
199        public ClientState(IBinder appToken) {
200            mAppToken = appToken;
201            mPid = Binder.getCallingPid();
202            if (appToken instanceof Binder) {
203                // For local clients, there is no reason to track them.
204                mStartedOps = null;
205            } else {
206                mStartedOps = new ArrayList<Op>();
207                try {
208                    mAppToken.linkToDeath(this, 0);
209                } catch (RemoteException e) {
210                }
211            }
212        }
213
214        @Override
215        public String toString() {
216            return "ClientState{" +
217                    "mAppToken=" + mAppToken +
218                    ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
219                    '}';
220        }
221
222        @Override
223        public void binderDied() {
224            synchronized (AppOpsService.this) {
225                for (int i=mStartedOps.size()-1; i>=0; i--) {
226                    finishOperationLocked(mStartedOps.get(i));
227                }
228                mClients.remove(mAppToken);
229            }
230        }
231    }
232
233    public AppOpsService(File storagePath, Handler handler) {
234        mFile = new AtomicFile(storagePath);
235        mHandler = handler;
236        readState();
237    }
238
239    public void publish(Context context) {
240        mContext = context;
241        ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
242    }
243
244    public void systemReady() {
245        synchronized (this) {
246            boolean changed = false;
247            for (int i = mUidStates.size() - 1; i >= 0; i--) {
248                UidState uidState = mUidStates.valueAt(i);
249
250                String[] packageNames = getPackagesForUid(uidState.uid);
251                if (ArrayUtils.isEmpty(packageNames)) {
252                    uidState.clear();
253                    mUidStates.removeAt(i);
254                    changed = true;
255                    continue;
256                }
257
258                ArrayMap<String, Ops> pkgs = uidState.pkgOps;
259                if (pkgs == null) {
260                    continue;
261                }
262
263                Iterator<Ops> it = pkgs.values().iterator();
264                while (it.hasNext()) {
265                    Ops ops = it.next();
266                    int curUid;
267                    try {
268                        curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
269                                UserHandle.getUserId(ops.uidState.uid));
270                    } catch (NameNotFoundException e) {
271                        curUid = -1;
272                    }
273                    if (curUid != ops.uidState.uid) {
274                        Slog.i(TAG, "Pruning old package " + ops.packageName
275                                + "/" + ops.uidState + ": new uid=" + curUid);
276                        it.remove();
277                        changed = true;
278                    }
279                }
280
281                if (uidState.isDefault()) {
282                    mUidStates.removeAt(i);
283                }
284            }
285            if (changed) {
286                scheduleFastWriteLocked();
287            }
288        }
289
290        MountServiceInternal mountServiceInternal = LocalServices.getService(
291                MountServiceInternal.class);
292        mountServiceInternal.addExternalStoragePolicy(
293                new MountServiceInternal.ExternalStorageMountPolicy() {
294                    @Override
295                    public int getMountMode(int uid, String packageName) {
296                        if (Process.isIsolated(uid)) {
297                            return Zygote.MOUNT_EXTERNAL_NONE;
298                        }
299                        if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
300                                packageName) != AppOpsManager.MODE_ALLOWED) {
301                            return Zygote.MOUNT_EXTERNAL_NONE;
302                        }
303                        if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
304                                packageName) != AppOpsManager.MODE_ALLOWED) {
305                            return Zygote.MOUNT_EXTERNAL_READ;
306                        }
307                        return Zygote.MOUNT_EXTERNAL_WRITE;
308                    }
309
310                    @Override
311                    public boolean hasExternalStorage(int uid, String packageName) {
312                        final int mountMode = getMountMode(uid, packageName);
313                        return mountMode == Zygote.MOUNT_EXTERNAL_READ
314                                || mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
315                    }
316                });
317    }
318
319    public void packageRemoved(int uid, String packageName) {
320        synchronized (this) {
321            UidState uidState = mUidStates.get(uid);
322            if (uidState == null) {
323                return;
324            }
325
326            boolean changed = false;
327
328            // Remove any package state if such.
329            if (uidState.pkgOps != null && uidState.pkgOps.remove(packageName) != null) {
330                changed = true;
331            }
332
333            // If we just nuked the last package state check if the UID is valid.
334            if (changed && uidState.pkgOps.isEmpty()
335                    && getPackagesForUid(uid).length <= 0) {
336                mUidStates.remove(uid);
337            }
338
339            if (changed) {
340                scheduleFastWriteLocked();
341            }
342        }
343    }
344
345    public void uidRemoved(int uid) {
346        synchronized (this) {
347            if (mUidStates.indexOfKey(uid) >= 0) {
348                mUidStates.remove(uid);
349                scheduleFastWriteLocked();
350            }
351        }
352    }
353
354    public void shutdown() {
355        Slog.w(TAG, "Writing app ops before shutdown...");
356        boolean doWrite = false;
357        synchronized (this) {
358            if (mWriteScheduled) {
359                mWriteScheduled = false;
360                doWrite = true;
361            }
362        }
363        if (doWrite) {
364            writeState();
365        }
366    }
367
368    private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
369        ArrayList<AppOpsManager.OpEntry> resOps = null;
370        if (ops == null) {
371            resOps = new ArrayList<AppOpsManager.OpEntry>();
372            for (int j=0; j<pkgOps.size(); j++) {
373                Op curOp = pkgOps.valueAt(j);
374                resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
375                        curOp.rejectTime, curOp.duration, curOp.proxyUid,
376                        curOp.proxyPackageName));
377            }
378        } else {
379            for (int j=0; j<ops.length; j++) {
380                Op curOp = pkgOps.get(ops[j]);
381                if (curOp != null) {
382                    if (resOps == null) {
383                        resOps = new ArrayList<AppOpsManager.OpEntry>();
384                    }
385                    resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
386                            curOp.rejectTime, curOp.duration, curOp.proxyUid,
387                            curOp.proxyPackageName));
388                }
389            }
390        }
391        return resOps;
392    }
393
394    @Override
395    public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
396        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
397                Binder.getCallingPid(), Binder.getCallingUid(), null);
398        ArrayList<AppOpsManager.PackageOps> res = null;
399        synchronized (this) {
400            final int uidStateCount = mUidStates.size();
401            for (int i = 0; i < uidStateCount; i++) {
402                UidState uidState = mUidStates.valueAt(i);
403                if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()) {
404                    continue;
405                }
406                ArrayMap<String, Ops> packages = uidState.pkgOps;
407                final int packageCount = packages.size();
408                for (int j = 0; j < packageCount; j++) {
409                    Ops pkgOps = packages.valueAt(j);
410                    ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
411                    if (resOps != null) {
412                        if (res == null) {
413                            res = new ArrayList<AppOpsManager.PackageOps>();
414                        }
415                        AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
416                                pkgOps.packageName, pkgOps.uidState.uid, resOps);
417                        res.add(resPackage);
418                    }
419                }
420            }
421        }
422        return res;
423    }
424
425    @Override
426    public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
427            int[] ops) {
428        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
429                Binder.getCallingPid(), Binder.getCallingUid(), null);
430        synchronized (this) {
431            Ops pkgOps = getOpsLocked(uid, packageName, false);
432            if (pkgOps == null) {
433                return null;
434            }
435            ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
436            if (resOps == null) {
437                return null;
438            }
439            ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
440            AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
441                    pkgOps.packageName, pkgOps.uidState.uid, resOps);
442            res.add(resPackage);
443            return res;
444        }
445    }
446
447    private void pruneOp(Op op, int uid, String packageName) {
448        if (op.time == 0 && op.rejectTime == 0) {
449            Ops ops = getOpsLocked(uid, packageName, false);
450            if (ops != null) {
451                ops.remove(op.op);
452                if (ops.size() <= 0) {
453                    UidState uidState = ops.uidState;
454                    ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
455                    if (pkgOps != null) {
456                        pkgOps.remove(ops.packageName);
457                        if (pkgOps.isEmpty()) {
458                            uidState.pkgOps = null;
459                        }
460                        if (uidState.isDefault()) {
461                            mUidStates.remove(uid);
462                        }
463                    }
464                }
465            }
466        }
467    }
468
469    @Override
470    public void setUidMode(int code, int uid, int mode) {
471        if (Binder.getCallingPid() != Process.myPid()) {
472            mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
473                    Binder.getCallingPid(), Binder.getCallingUid(), null);
474        }
475        verifyIncomingOp(code);
476        code = AppOpsManager.opToSwitch(code);
477
478        synchronized (this) {
479            final int defaultMode = AppOpsManager.opToDefaultMode(code);
480
481            UidState uidState = getUidStateLocked(uid, false);
482            if (uidState == null) {
483                if (mode == defaultMode) {
484                    return;
485                }
486                uidState = new UidState(uid);
487                uidState.opModes = new SparseIntArray();
488                uidState.opModes.put(code, mode);
489                mUidStates.put(uid, uidState);
490                scheduleWriteLocked();
491            } else if (uidState.opModes == null) {
492                if (mode != defaultMode) {
493                    uidState.opModes = new SparseIntArray();
494                    uidState.opModes.put(code, mode);
495                    scheduleWriteLocked();
496                }
497            } else {
498                if (uidState.opModes.get(code) == mode) {
499                    return;
500                }
501                if (mode == defaultMode) {
502                    uidState.opModes.delete(code);
503                    if (uidState.opModes.size() <= 0) {
504                        uidState.opModes = null;
505                    }
506                } else {
507                    uidState.opModes.put(code, mode);
508                }
509                scheduleWriteLocked();
510            }
511        }
512
513        String[] uidPackageNames = getPackagesForUid(uid);
514        ArrayMap<Callback, ArraySet<String>> callbackSpecs = null;
515
516        ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
517        if (callbacks != null) {
518            final int callbackCount = callbacks.size();
519            for (int i = 0; i < callbackCount; i++) {
520                Callback callback = callbacks.get(i);
521                ArraySet<String> changedPackages = new ArraySet<>();
522                Collections.addAll(changedPackages, uidPackageNames);
523                callbackSpecs = new ArrayMap<>();
524                callbackSpecs.put(callback, changedPackages);
525            }
526        }
527
528        for (String uidPackageName : uidPackageNames) {
529            callbacks = mPackageModeWatchers.get(uidPackageName);
530            if (callbacks != null) {
531                if (callbackSpecs == null) {
532                    callbackSpecs = new ArrayMap<>();
533                }
534                final int callbackCount = callbacks.size();
535                for (int i = 0; i < callbackCount; i++) {
536                    Callback callback = callbacks.get(i);
537                    ArraySet<String> changedPackages = callbackSpecs.get(callback);
538                    if (changedPackages == null) {
539                        changedPackages = new ArraySet<>();
540                        callbackSpecs.put(callback, changedPackages);
541                    }
542                    changedPackages.add(uidPackageName);
543                }
544            }
545        }
546
547        if (callbackSpecs == null) {
548            return;
549        }
550
551        // There are components watching for mode changes such as window manager
552        // and location manager which are in our process. The callbacks in these
553        // components may require permissions our remote caller does not have.
554        final long identity = Binder.clearCallingIdentity();
555        try {
556            for (int i = 0; i < callbackSpecs.size(); i++) {
557                Callback callback = callbackSpecs.keyAt(i);
558                ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
559                try {
560                    if (reportedPackageNames == null) {
561                        callback.mCallback.opChanged(code, null);
562                    } else {
563                        final int reportedPackageCount = reportedPackageNames.size();
564                        for (int j = 0; j < reportedPackageCount; j++) {
565                            String reportedPackageName = reportedPackageNames.valueAt(j);
566                            callback.mCallback.opChanged(code, reportedPackageName);
567                        }
568                    }
569                } catch (RemoteException e) {
570                    Log.w(TAG, "Error dispatching op op change", e);
571                }
572            }
573        } finally {
574            Binder.restoreCallingIdentity(identity);
575        }
576    }
577
578    @Override
579    public void setMode(int code, int uid, String packageName, int mode) {
580        if (Binder.getCallingPid() != Process.myPid()) {
581            mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
582                    Binder.getCallingPid(), Binder.getCallingUid(), null);
583        }
584        verifyIncomingOp(code);
585        ArrayList<Callback> repCbs = null;
586        code = AppOpsManager.opToSwitch(code);
587        synchronized (this) {
588            UidState uidState = getUidStateLocked(uid, false);
589            Op op = getOpLocked(code, uid, packageName, true);
590            if (op != null) {
591                if (op.mode != mode) {
592                    op.mode = mode;
593                    ArrayList<Callback> cbs = mOpModeWatchers.get(code);
594                    if (cbs != null) {
595                        if (repCbs == null) {
596                            repCbs = new ArrayList<Callback>();
597                        }
598                        repCbs.addAll(cbs);
599                    }
600                    cbs = mPackageModeWatchers.get(packageName);
601                    if (cbs != null) {
602                        if (repCbs == null) {
603                            repCbs = new ArrayList<Callback>();
604                        }
605                        repCbs.addAll(cbs);
606                    }
607                    if (mode == AppOpsManager.opToDefaultMode(op.op)) {
608                        // If going into the default mode, prune this op
609                        // if there is nothing else interesting in it.
610                        pruneOp(op, uid, packageName);
611                    }
612                    scheduleFastWriteLocked();
613                }
614            }
615        }
616        if (repCbs != null) {
617            // There are components watching for mode changes such as window manager
618            // and location manager which are in our process. The callbacks in these
619            // components may require permissions our remote caller does not have.
620            final long identity = Binder.clearCallingIdentity();
621            try {
622                for (int i = 0; i < repCbs.size(); i++) {
623                    try {
624                        repCbs.get(i).mCallback.opChanged(code, packageName);
625                    } catch (RemoteException e) {
626                    }
627                }
628            } finally {
629                Binder.restoreCallingIdentity(identity);
630            }
631        }
632    }
633
634    private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
635            HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
636            String packageName, int op, ArrayList<Callback> cbs) {
637        if (cbs == null) {
638            return callbacks;
639        }
640        if (callbacks == null) {
641            callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
642        }
643        boolean duplicate = false;
644        for (int i=0; i<cbs.size(); i++) {
645            Callback cb = cbs.get(i);
646            ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
647            if (reports == null) {
648                reports = new ArrayList<Pair<String, Integer>>();
649                callbacks.put(cb, reports);
650            } else {
651                final int reportCount = reports.size();
652                for (int j = 0; j < reportCount; j++) {
653                    Pair<String, Integer> report = reports.get(j);
654                    if (report.second == op && report.first.equals(packageName)) {
655                        duplicate = true;
656                        break;
657                    }
658                }
659            }
660            if (!duplicate) {
661                reports.add(new Pair<>(packageName, op));
662            }
663        }
664        return callbacks;
665    }
666
667    @Override
668    public void resetAllModes(int reqUserId, String reqPackageName) {
669        final int callingPid = Binder.getCallingPid();
670        final int callingUid = Binder.getCallingUid();
671        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
672                callingPid, callingUid, null);
673        reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId,
674                true, true, "resetAllModes", null);
675
676        int reqUid = -1;
677        if (reqPackageName != null) {
678            try {
679                reqUid = AppGlobals.getPackageManager().getPackageUid(
680                        reqPackageName, reqUserId);
681            } catch (RemoteException e) {
682                /* ignore - local call */
683            }
684        }
685
686        HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
687        synchronized (this) {
688            boolean changed = false;
689            for (int i = mUidStates.size() - 1; i >= 0; i--) {
690                UidState uidState = mUidStates.valueAt(i);
691
692                SparseIntArray opModes = uidState.opModes;
693                if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
694                    final int uidOpCount = opModes.size();
695                    for (int j = uidOpCount - 1; j >= 0; j--) {
696                        final int code = opModes.keyAt(j);
697                        if (AppOpsManager.opAllowsReset(code)) {
698                            opModes.removeAt(j);
699                            if (opModes.size() <= 0) {
700                                uidState.opModes = null;
701                            }
702                            for (String packageName : getPackagesForUid(uidState.uid)) {
703                                callbacks = addCallbacks(callbacks, packageName, code,
704                                        mOpModeWatchers.get(code));
705                                callbacks = addCallbacks(callbacks, packageName, code,
706                                        mPackageModeWatchers.get(packageName));
707                            }
708                        }
709                    }
710                }
711
712                if (uidState.pkgOps == null) {
713                    continue;
714                }
715
716                if (reqUserId != UserHandle.USER_ALL
717                        && reqUserId != UserHandle.getUserId(uidState.uid)) {
718                    // Skip any ops for a different user
719                    continue;
720                }
721
722                Map<String, Ops> packages = uidState.pkgOps;
723                Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
724                while (it.hasNext()) {
725                    Map.Entry<String, Ops> ent = it.next();
726                    String packageName = ent.getKey();
727                    if (reqPackageName != null && !reqPackageName.equals(packageName)) {
728                        // Skip any ops for a different package
729                        continue;
730                    }
731                    Ops pkgOps = ent.getValue();
732                    for (int j=pkgOps.size()-1; j>=0; j--) {
733                        Op curOp = pkgOps.valueAt(j);
734                        if (AppOpsManager.opAllowsReset(curOp.op)
735                                && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
736                            curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
737                            changed = true;
738                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
739                                    mOpModeWatchers.get(curOp.op));
740                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
741                                    mPackageModeWatchers.get(packageName));
742                            if (curOp.time == 0 && curOp.rejectTime == 0) {
743                                pkgOps.removeAt(j);
744                            }
745                        }
746                    }
747                    if (pkgOps.size() == 0) {
748                        it.remove();
749                    }
750                }
751                if (uidState.isDefault()) {
752                    mUidStates.remove(uidState.uid);
753                }
754            }
755
756            if (changed) {
757                scheduleFastWriteLocked();
758            }
759        }
760        if (callbacks != null) {
761            for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
762                Callback cb = ent.getKey();
763                ArrayList<Pair<String, Integer>> reports = ent.getValue();
764                for (int i=0; i<reports.size(); i++) {
765                    Pair<String, Integer> rep = reports.get(i);
766                    try {
767                        cb.mCallback.opChanged(rep.second, rep.first);
768                    } catch (RemoteException e) {
769                    }
770                }
771            }
772        }
773    }
774
775    @Override
776    public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
777        synchronized (this) {
778            op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
779            Callback cb = mModeWatchers.get(callback.asBinder());
780            if (cb == null) {
781                cb = new Callback(callback);
782                mModeWatchers.put(callback.asBinder(), cb);
783            }
784            if (op != AppOpsManager.OP_NONE) {
785                ArrayList<Callback> cbs = mOpModeWatchers.get(op);
786                if (cbs == null) {
787                    cbs = new ArrayList<Callback>();
788                    mOpModeWatchers.put(op, cbs);
789                }
790                cbs.add(cb);
791            }
792            if (packageName != null) {
793                ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
794                if (cbs == null) {
795                    cbs = new ArrayList<Callback>();
796                    mPackageModeWatchers.put(packageName, cbs);
797                }
798                cbs.add(cb);
799            }
800        }
801    }
802
803    @Override
804    public void stopWatchingMode(IAppOpsCallback callback) {
805        synchronized (this) {
806            Callback cb = mModeWatchers.remove(callback.asBinder());
807            if (cb != null) {
808                cb.unlinkToDeath();
809                for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
810                    ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
811                    cbs.remove(cb);
812                    if (cbs.size() <= 0) {
813                        mOpModeWatchers.removeAt(i);
814                    }
815                }
816                for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
817                    ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
818                    cbs.remove(cb);
819                    if (cbs.size() <= 0) {
820                        mPackageModeWatchers.removeAt(i);
821                    }
822                }
823            }
824        }
825    }
826
827    @Override
828    public IBinder getToken(IBinder clientToken) {
829        synchronized (this) {
830            ClientState cs = mClients.get(clientToken);
831            if (cs == null) {
832                cs = new ClientState(clientToken);
833                mClients.put(clientToken, cs);
834            }
835            return cs;
836        }
837    }
838
839    @Override
840    public int checkOperation(int code, int uid, String packageName) {
841        verifyIncomingUid(uid);
842        verifyIncomingOp(code);
843        synchronized (this) {
844            if (isOpRestricted(uid, code, packageName)) {
845                return AppOpsManager.MODE_IGNORED;
846            }
847            code = AppOpsManager.opToSwitch(code);
848            UidState uidState = getUidStateLocked(uid, false);
849            if (uidState != null && uidState.opModes != null) {
850                final int uidMode = uidState.opModes.get(code);
851                if (uidMode != AppOpsManager.MODE_ALLOWED) {
852                    return uidMode;
853                }
854            }
855            Op op = getOpLocked(code, uid, packageName, false);
856            if (op == null) {
857                return AppOpsManager.opToDefaultMode(code);
858            }
859            return op.mode;
860        }
861    }
862
863    @Override
864    public int checkAudioOperation(int code, int usage, int uid, String packageName) {
865        synchronized (this) {
866            final int mode = checkRestrictionLocked(code, usage, uid, packageName);
867            if (mode != AppOpsManager.MODE_ALLOWED) {
868                return mode;
869            }
870        }
871        return checkOperation(code, uid, packageName);
872    }
873
874    private int checkRestrictionLocked(int code, int usage, int uid, String packageName) {
875        final SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
876        if (usageRestrictions != null) {
877            final Restriction r = usageRestrictions.get(usage);
878            if (r != null && !r.exceptionPackages.contains(packageName)) {
879                return r.mode;
880            }
881        }
882        return AppOpsManager.MODE_ALLOWED;
883    }
884
885    @Override
886    public void setAudioRestriction(int code, int usage, int uid, int mode,
887            String[] exceptionPackages) {
888        verifyIncomingUid(uid);
889        verifyIncomingOp(code);
890        synchronized (this) {
891            SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
892            if (usageRestrictions == null) {
893                usageRestrictions = new SparseArray<Restriction>();
894                mAudioRestrictions.put(code, usageRestrictions);
895            }
896            usageRestrictions.remove(usage);
897            if (mode != AppOpsManager.MODE_ALLOWED) {
898                final Restriction r = new Restriction();
899                r.mode = mode;
900                if (exceptionPackages != null) {
901                    final int N = exceptionPackages.length;
902                    r.exceptionPackages = new ArraySet<String>(N);
903                    for (int i = 0; i < N; i++) {
904                        final String pkg = exceptionPackages[i];
905                        if (pkg != null) {
906                            r.exceptionPackages.add(pkg.trim());
907                        }
908                    }
909                }
910                usageRestrictions.put(usage, r);
911            }
912        }
913    }
914
915    @Override
916    public int checkPackage(int uid, String packageName) {
917        synchronized (this) {
918            if (getOpsRawLocked(uid, packageName, true) != null) {
919                return AppOpsManager.MODE_ALLOWED;
920            } else {
921                return AppOpsManager.MODE_ERRORED;
922            }
923        }
924    }
925
926    @Override
927    public int noteProxyOperation(int code, String proxyPackageName,
928            int proxiedUid, String proxiedPackageName) {
929        verifyIncomingOp(code);
930        final int proxyMode = noteOperationUnchecked(code, Binder.getCallingUid(),
931                proxyPackageName, -1, null);
932        if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
933            return proxyMode;
934        }
935        return noteOperationUnchecked(code, proxiedUid, proxiedPackageName,
936                Binder.getCallingUid(), proxyPackageName);
937    }
938
939    @Override
940    public int noteOperation(int code, int uid, String packageName) {
941        verifyIncomingUid(uid);
942        verifyIncomingOp(code);
943        return noteOperationUnchecked(code, uid, packageName, 0, null);
944    }
945
946    private int noteOperationUnchecked(int code, int uid, String packageName,
947            int proxyUid, String proxyPackageName) {
948        synchronized (this) {
949            Ops ops = getOpsLocked(uid, packageName, true);
950            if (ops == null) {
951                if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
952                        + " package " + packageName);
953                return AppOpsManager.MODE_ERRORED;
954            }
955            Op op = getOpLocked(ops, code, true);
956            if (isOpRestricted(uid, code, packageName)) {
957                return AppOpsManager.MODE_IGNORED;
958            }
959            if (op.duration == -1) {
960                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
961                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
962            }
963            op.duration = 0;
964            final int switchCode = AppOpsManager.opToSwitch(code);
965            UidState uidState = ops.uidState;
966            if (uidState.opModes != null) {
967                final int uidMode = uidState.opModes.get(switchCode);
968                if (uidMode != AppOpsManager.MODE_ALLOWED) {
969                    if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
970                            + switchCode + " (" + code + ") uid " + uid + " package "
971                            + packageName);
972                    op.rejectTime = System.currentTimeMillis();
973                    return uidMode;
974                }
975            }
976            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
977            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
978                if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
979                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
980                op.rejectTime = System.currentTimeMillis();
981                return switchOp.mode;
982            }
983            if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
984                    + " package " + packageName);
985            op.time = System.currentTimeMillis();
986            op.rejectTime = 0;
987            op.proxyUid = proxyUid;
988            op.proxyPackageName = proxyPackageName;
989            return AppOpsManager.MODE_ALLOWED;
990        }
991    }
992
993    @Override
994    public int startOperation(IBinder token, int code, int uid, String packageName) {
995        verifyIncomingUid(uid);
996        verifyIncomingOp(code);
997        ClientState client = (ClientState)token;
998        synchronized (this) {
999            Ops ops = getOpsLocked(uid, packageName, true);
1000            if (ops == null) {
1001                if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
1002                        + " package " + packageName);
1003                return AppOpsManager.MODE_ERRORED;
1004            }
1005            Op op = getOpLocked(ops, code, true);
1006            if (isOpRestricted(uid, code, packageName)) {
1007                return AppOpsManager.MODE_IGNORED;
1008            }
1009            final int switchCode = AppOpsManager.opToSwitch(code);
1010            UidState uidState = ops.uidState;
1011            if (uidState.opModes != null) {
1012                final int uidMode = uidState.opModes.get(switchCode);
1013                if (uidMode != AppOpsManager.MODE_ALLOWED) {
1014                    if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
1015                            + switchCode + " (" + code + ") uid " + uid + " package "
1016                            + packageName);
1017                    op.rejectTime = System.currentTimeMillis();
1018                    return uidMode;
1019                }
1020            }
1021            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
1022            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
1023                if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
1024                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
1025                op.rejectTime = System.currentTimeMillis();
1026                return switchOp.mode;
1027            }
1028            if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
1029                    + " package " + packageName);
1030            if (op.nesting == 0) {
1031                op.time = System.currentTimeMillis();
1032                op.rejectTime = 0;
1033                op.duration = -1;
1034            }
1035            op.nesting++;
1036            if (client.mStartedOps != null) {
1037                client.mStartedOps.add(op);
1038            }
1039            return AppOpsManager.MODE_ALLOWED;
1040        }
1041    }
1042
1043    @Override
1044    public void finishOperation(IBinder token, int code, int uid, String packageName) {
1045        verifyIncomingUid(uid);
1046        verifyIncomingOp(code);
1047        ClientState client = (ClientState)token;
1048        synchronized (this) {
1049            Op op = getOpLocked(code, uid, packageName, true);
1050            if (op == null) {
1051                return;
1052            }
1053            if (client.mStartedOps != null) {
1054                if (!client.mStartedOps.remove(op)) {
1055                    throw new IllegalStateException("Operation not started: uid" + op.uid
1056                            + " pkg=" + op.packageName + " op=" + op.op);
1057                }
1058            }
1059            finishOperationLocked(op);
1060        }
1061    }
1062
1063    @Override
1064    public int permissionToOpCode(String permission) {
1065        return AppOpsManager.permissionToOpCode(permission);
1066    }
1067
1068    void finishOperationLocked(Op op) {
1069        if (op.nesting <= 1) {
1070            if (op.nesting == 1) {
1071                op.duration = (int)(System.currentTimeMillis() - op.time);
1072                op.time += op.duration;
1073            } else {
1074                Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
1075                        + op.packageName + " code " + op.op + " time=" + op.time
1076                        + " duration=" + op.duration + " nesting=" + op.nesting);
1077            }
1078            op.nesting = 0;
1079        } else {
1080            op.nesting--;
1081        }
1082    }
1083
1084    private void verifyIncomingUid(int uid) {
1085        if (uid == Binder.getCallingUid()) {
1086            return;
1087        }
1088        if (Binder.getCallingPid() == Process.myPid()) {
1089            return;
1090        }
1091        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
1092                Binder.getCallingPid(), Binder.getCallingUid(), null);
1093    }
1094
1095    private void verifyIncomingOp(int op) {
1096        if (op >= 0 && op < AppOpsManager._NUM_OP) {
1097            return;
1098        }
1099        throw new IllegalArgumentException("Bad operation #" + op);
1100    }
1101
1102    private UidState getUidStateLocked(int uid, boolean edit) {
1103        UidState uidState = mUidStates.get(uid);
1104        if (uidState == null) {
1105            if (!edit) {
1106                return null;
1107            }
1108            uidState = new UidState(uid);
1109            mUidStates.put(uid, uidState);
1110        }
1111        return uidState;
1112    }
1113
1114    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
1115        if (uid == 0) {
1116            packageName = "root";
1117        } else if (uid == Process.SHELL_UID) {
1118            packageName = "com.android.shell";
1119        }
1120        return getOpsRawLocked(uid, packageName, edit);
1121    }
1122
1123    private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
1124        UidState uidState = getUidStateLocked(uid, edit);
1125        if (uidState == null) {
1126            return null;
1127        }
1128
1129        if (uidState.pkgOps == null) {
1130            if (!edit) {
1131                return null;
1132            }
1133            uidState.pkgOps = new ArrayMap<>();
1134        }
1135
1136        Ops ops = uidState.pkgOps.get(packageName);
1137        if (ops == null) {
1138            if (!edit) {
1139                return null;
1140            }
1141            boolean isPrivileged = false;
1142            // This is the first time we have seen this package name under this uid,
1143            // so let's make sure it is valid.
1144            if (uid != 0) {
1145                final long ident = Binder.clearCallingIdentity();
1146                try {
1147                    int pkgUid = -1;
1148                    try {
1149                        ApplicationInfo appInfo = ActivityThread.getPackageManager()
1150                                .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid));
1151                        if (appInfo != null) {
1152                            pkgUid = appInfo.uid;
1153                            isPrivileged = (appInfo.privateFlags
1154                                    & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
1155                        } else {
1156                            if ("media".equals(packageName)) {
1157                                pkgUid = Process.MEDIA_UID;
1158                                isPrivileged = false;
1159                            }
1160                        }
1161                    } catch (RemoteException e) {
1162                        Slog.w(TAG, "Could not contact PackageManager", e);
1163                    }
1164                    if (pkgUid != uid) {
1165                        // Oops!  The package name is not valid for the uid they are calling
1166                        // under.  Abort.
1167                        Slog.w(TAG, "Bad call: specified package " + packageName
1168                                + " under uid " + uid + " but it is really " + pkgUid);
1169                        return null;
1170                    }
1171                } finally {
1172                    Binder.restoreCallingIdentity(ident);
1173                }
1174            }
1175            ops = new Ops(packageName, uidState, isPrivileged);
1176            uidState.pkgOps.put(packageName, ops);
1177        }
1178        return ops;
1179    }
1180
1181    private void scheduleWriteLocked() {
1182        if (!mWriteScheduled) {
1183            mWriteScheduled = true;
1184            mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
1185        }
1186    }
1187
1188    private void scheduleFastWriteLocked() {
1189        if (!mFastWriteScheduled) {
1190            mWriteScheduled = true;
1191            mFastWriteScheduled = true;
1192            mHandler.removeCallbacks(mWriteRunner);
1193            mHandler.postDelayed(mWriteRunner, 10*1000);
1194        }
1195    }
1196
1197    private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
1198        Ops ops = getOpsLocked(uid, packageName, edit);
1199        if (ops == null) {
1200            return null;
1201        }
1202        return getOpLocked(ops, code, edit);
1203    }
1204
1205    private Op getOpLocked(Ops ops, int code, boolean edit) {
1206        Op op = ops.get(code);
1207        if (op == null) {
1208            if (!edit) {
1209                return null;
1210            }
1211            op = new Op(ops.uidState.uid, ops.packageName, code);
1212            ops.put(code, op);
1213        }
1214        if (edit) {
1215            scheduleWriteLocked();
1216        }
1217        return op;
1218    }
1219
1220    private boolean isOpRestricted(int uid, int code, String packageName) {
1221        int userHandle = UserHandle.getUserId(uid);
1222        boolean[] opRestrictions = mOpRestrictions.get(userHandle);
1223        if ((opRestrictions != null) && opRestrictions[code]) {
1224            if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
1225                synchronized (this) {
1226                    Ops ops = getOpsLocked(uid, packageName, true);
1227                    if ((ops != null) && ops.isPrivileged) {
1228                        return false;
1229                    }
1230                }
1231            }
1232            return true;
1233        }
1234        return false;
1235    }
1236
1237    void readState() {
1238        synchronized (mFile) {
1239            synchronized (this) {
1240                FileInputStream stream;
1241                try {
1242                    stream = mFile.openRead();
1243                } catch (FileNotFoundException e) {
1244                    Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
1245                    return;
1246                }
1247                boolean success = false;
1248                mUidStates.clear();
1249                try {
1250                    XmlPullParser parser = Xml.newPullParser();
1251                    parser.setInput(stream, StandardCharsets.UTF_8.name());
1252                    int type;
1253                    while ((type = parser.next()) != XmlPullParser.START_TAG
1254                            && type != XmlPullParser.END_DOCUMENT) {
1255                        ;
1256                    }
1257
1258                    if (type != XmlPullParser.START_TAG) {
1259                        throw new IllegalStateException("no start tag found");
1260                    }
1261
1262                    int outerDepth = parser.getDepth();
1263                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1264                            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1265                        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1266                            continue;
1267                        }
1268
1269                        String tagName = parser.getName();
1270                        if (tagName.equals("pkg")) {
1271                            readPackage(parser);
1272                        } else if (tagName.equals("uid")) {
1273                            readUidOps(parser);
1274                        } else {
1275                            Slog.w(TAG, "Unknown element under <app-ops>: "
1276                                    + parser.getName());
1277                            XmlUtils.skipCurrentTag(parser);
1278                        }
1279                    }
1280                    success = true;
1281                } catch (IllegalStateException e) {
1282                    Slog.w(TAG, "Failed parsing " + e);
1283                } catch (NullPointerException e) {
1284                    Slog.w(TAG, "Failed parsing " + e);
1285                } catch (NumberFormatException e) {
1286                    Slog.w(TAG, "Failed parsing " + e);
1287                } catch (XmlPullParserException e) {
1288                    Slog.w(TAG, "Failed parsing " + e);
1289                } catch (IOException e) {
1290                    Slog.w(TAG, "Failed parsing " + e);
1291                } catch (IndexOutOfBoundsException e) {
1292                    Slog.w(TAG, "Failed parsing " + e);
1293                } finally {
1294                    if (!success) {
1295                        mUidStates.clear();
1296                    }
1297                    try {
1298                        stream.close();
1299                    } catch (IOException e) {
1300                    }
1301                }
1302            }
1303        }
1304    }
1305
1306    void readUidOps(XmlPullParser parser) throws NumberFormatException,
1307            XmlPullParserException, IOException {
1308        final int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
1309        int outerDepth = parser.getDepth();
1310        int type;
1311        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1312                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1313            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1314                continue;
1315            }
1316
1317            String tagName = parser.getName();
1318            if (tagName.equals("op")) {
1319                final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
1320                final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
1321                UidState uidState = getUidStateLocked(uid, true);
1322                if (uidState.opModes == null) {
1323                    uidState.opModes = new SparseIntArray();
1324                }
1325                uidState.opModes.put(code, mode);
1326            } else {
1327                Slog.w(TAG, "Unknown element under <uid-ops>: "
1328                        + parser.getName());
1329                XmlUtils.skipCurrentTag(parser);
1330            }
1331        }
1332    }
1333
1334    void readPackage(XmlPullParser parser) throws NumberFormatException,
1335            XmlPullParserException, IOException {
1336        String pkgName = parser.getAttributeValue(null, "n");
1337        int outerDepth = parser.getDepth();
1338        int type;
1339        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1340                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1341            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1342                continue;
1343            }
1344
1345            String tagName = parser.getName();
1346            if (tagName.equals("uid")) {
1347                readUid(parser, pkgName);
1348            } else {
1349                Slog.w(TAG, "Unknown element under <pkg>: "
1350                        + parser.getName());
1351                XmlUtils.skipCurrentTag(parser);
1352            }
1353        }
1354    }
1355
1356    void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
1357            XmlPullParserException, IOException {
1358        int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
1359        String isPrivilegedString = parser.getAttributeValue(null, "p");
1360        boolean isPrivileged = false;
1361        if (isPrivilegedString == null) {
1362            try {
1363                IPackageManager packageManager = ActivityThread.getPackageManager();
1364                if (packageManager != null) {
1365                    ApplicationInfo appInfo = ActivityThread.getPackageManager()
1366                            .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
1367                    if (appInfo != null) {
1368                        isPrivileged = (appInfo.privateFlags
1369                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
1370                    }
1371                } else {
1372                    // Could not load data, don't add to cache so it will be loaded later.
1373                    return;
1374                }
1375            } catch (RemoteException e) {
1376                Slog.w(TAG, "Could not contact PackageManager", e);
1377            }
1378        } else {
1379            isPrivileged = Boolean.parseBoolean(isPrivilegedString);
1380        }
1381        int outerDepth = parser.getDepth();
1382        int type;
1383        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1384                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1385            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1386                continue;
1387            }
1388
1389            String tagName = parser.getName();
1390            if (tagName.equals("op")) {
1391                Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
1392                String mode = parser.getAttributeValue(null, "m");
1393                if (mode != null) {
1394                    op.mode = Integer.parseInt(mode);
1395                }
1396                String time = parser.getAttributeValue(null, "t");
1397                if (time != null) {
1398                    op.time = Long.parseLong(time);
1399                }
1400                time = parser.getAttributeValue(null, "r");
1401                if (time != null) {
1402                    op.rejectTime = Long.parseLong(time);
1403                }
1404                String dur = parser.getAttributeValue(null, "d");
1405                if (dur != null) {
1406                    op.duration = Integer.parseInt(dur);
1407                }
1408                String proxyUid = parser.getAttributeValue(null, "pu");
1409                if (proxyUid != null) {
1410                    op.proxyUid = Integer.parseInt(proxyUid);
1411                }
1412                String proxyPackageName = parser.getAttributeValue(null, "pp");
1413                if (proxyPackageName != null) {
1414                    op.proxyPackageName = proxyPackageName;
1415                }
1416
1417                UidState uidState = getUidStateLocked(uid, true);
1418                if (uidState.pkgOps == null) {
1419                    uidState.pkgOps = new ArrayMap<>();
1420                }
1421
1422                Ops ops = uidState.pkgOps.get(pkgName);
1423                if (ops == null) {
1424                    ops = new Ops(pkgName, uidState, isPrivileged);
1425                    uidState.pkgOps.put(pkgName, ops);
1426                }
1427                ops.put(op.op, op);
1428            } else {
1429                Slog.w(TAG, "Unknown element under <pkg>: "
1430                        + parser.getName());
1431                XmlUtils.skipCurrentTag(parser);
1432            }
1433        }
1434    }
1435
1436    void writeState() {
1437        synchronized (mFile) {
1438            List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
1439
1440            FileOutputStream stream;
1441            try {
1442                stream = mFile.startWrite();
1443            } catch (IOException e) {
1444                Slog.w(TAG, "Failed to write state: " + e);
1445                return;
1446            }
1447
1448            try {
1449                XmlSerializer out = new FastXmlSerializer();
1450                out.setOutput(stream, StandardCharsets.UTF_8.name());
1451                out.startDocument(null, true);
1452                out.startTag(null, "app-ops");
1453
1454                final int uidStateCount = mUidStates.size();
1455                for (int i = 0; i < uidStateCount; i++) {
1456                    UidState uidState = mUidStates.valueAt(i);
1457                    if (uidState.opModes != null && uidState.opModes.size() > 0) {
1458                        out.startTag(null, "uid");
1459                        out.attribute(null, "n", Integer.toString(uidState.uid));
1460                        SparseIntArray uidOpModes = uidState.opModes;
1461                        final int opCount = uidOpModes.size();
1462                        for (int j = 0; j < opCount; j++) {
1463                            final int op = uidOpModes.keyAt(j);
1464                            final int mode = uidOpModes.valueAt(j);
1465                            out.startTag(null, "op");
1466                            out.attribute(null, "n", Integer.toString(op));
1467                            out.attribute(null, "m", Integer.toString(mode));
1468                            out.endTag(null, "op");
1469                        }
1470                        out.endTag(null, "uid");
1471                    }
1472                }
1473
1474                if (allOps != null) {
1475                    String lastPkg = null;
1476                    for (int i=0; i<allOps.size(); i++) {
1477                        AppOpsManager.PackageOps pkg = allOps.get(i);
1478                        if (!pkg.getPackageName().equals(lastPkg)) {
1479                            if (lastPkg != null) {
1480                                out.endTag(null, "pkg");
1481                            }
1482                            lastPkg = pkg.getPackageName();
1483                            out.startTag(null, "pkg");
1484                            out.attribute(null, "n", lastPkg);
1485                        }
1486                        out.startTag(null, "uid");
1487                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
1488                        synchronized (this) {
1489                            Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
1490                            // Should always be present as the list of PackageOps is generated
1491                            // from Ops.
1492                            if (ops != null) {
1493                                out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
1494                            } else {
1495                                out.attribute(null, "p", Boolean.toString(false));
1496                            }
1497                        }
1498                        List<AppOpsManager.OpEntry> ops = pkg.getOps();
1499                        for (int j=0; j<ops.size(); j++) {
1500                            AppOpsManager.OpEntry op = ops.get(j);
1501                            out.startTag(null, "op");
1502                            out.attribute(null, "n", Integer.toString(op.getOp()));
1503                            if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
1504                                out.attribute(null, "m", Integer.toString(op.getMode()));
1505                            }
1506                            long time = op.getTime();
1507                            if (time != 0) {
1508                                out.attribute(null, "t", Long.toString(time));
1509                            }
1510                            time = op.getRejectTime();
1511                            if (time != 0) {
1512                                out.attribute(null, "r", Long.toString(time));
1513                            }
1514                            int dur = op.getDuration();
1515                            if (dur != 0) {
1516                                out.attribute(null, "d", Integer.toString(dur));
1517                            }
1518                            int proxyUid = op.getProxyUid();
1519                            if (proxyUid != -1) {
1520                                out.attribute(null, "pu", Integer.toString(proxyUid));
1521                            }
1522                            String proxyPackageName = op.getProxyPackageName();
1523                            if (proxyPackageName != null) {
1524                                out.attribute(null, "pp", proxyPackageName);
1525                            }
1526                            out.endTag(null, "op");
1527                        }
1528                        out.endTag(null, "uid");
1529                    }
1530                    if (lastPkg != null) {
1531                        out.endTag(null, "pkg");
1532                    }
1533                }
1534
1535                out.endTag(null, "app-ops");
1536                out.endDocument();
1537                mFile.finishWrite(stream);
1538            } catch (IOException e) {
1539                Slog.w(TAG, "Failed to write state, restoring backup.", e);
1540                mFile.failWrite(stream);
1541            }
1542        }
1543    }
1544
1545    private void dumpHelp(PrintWriter pw) {
1546        pw.println("AppOps service (appops) dump options:");
1547        pw.println("  [-h] [CMD]");
1548        pw.println("  -h: print this help text.");
1549        pw.println("Commands:");
1550        pw.println("  write-settings");
1551        pw.println("    Immediately write pending changes to storage.");
1552        pw.println("  read-settings");
1553        pw.println("    Read the last written settings, replacing current state in RAM.");
1554    }
1555
1556    @Override
1557    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1558        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1559                != PackageManager.PERMISSION_GRANTED) {
1560            pw.println("Permission Denial: can't dump ApOps service from from pid="
1561                    + Binder.getCallingPid()
1562                    + ", uid=" + Binder.getCallingUid());
1563            return;
1564        }
1565
1566        if (args != null) {
1567            for (int i=0; i<args.length; i++) {
1568                String arg = args[i];
1569                if ("-h".equals(arg)) {
1570                    dumpHelp(pw);
1571                    return;
1572                } else if ("-a".equals(arg)) {
1573                    // dump all data
1574                } else if ("write-settings".equals(arg)) {
1575                    long token = Binder.clearCallingIdentity();
1576                    try {
1577                        synchronized (this) {
1578                            mHandler.removeCallbacks(mWriteRunner);
1579                        }
1580                        writeState();
1581                        pw.println("Current settings written.");
1582                    } finally {
1583                        Binder.restoreCallingIdentity(token);
1584                    }
1585                    return;
1586                } else if ("read-settings".equals(arg)) {
1587                    long token = Binder.clearCallingIdentity();
1588                    try {
1589                        readState();
1590                        pw.println("Last settings read.");
1591                    } finally {
1592                        Binder.restoreCallingIdentity(token);
1593                    }
1594                    return;
1595                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
1596                    pw.println("Unknown option: " + arg);
1597                    return;
1598                } else {
1599                    pw.println("Unknown command: " + arg);
1600                    return;
1601                }
1602            }
1603        }
1604
1605        synchronized (this) {
1606            pw.println("Current AppOps Service state:");
1607            final long now = System.currentTimeMillis();
1608            boolean needSep = false;
1609            if (mOpModeWatchers.size() > 0) {
1610                needSep = true;
1611                pw.println("  Op mode watchers:");
1612                for (int i=0; i<mOpModeWatchers.size(); i++) {
1613                    pw.print("    Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
1614                    pw.println(":");
1615                    ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
1616                    for (int j=0; j<callbacks.size(); j++) {
1617                        pw.print("      #"); pw.print(j); pw.print(": ");
1618                        pw.println(callbacks.get(j));
1619                    }
1620                }
1621            }
1622            if (mPackageModeWatchers.size() > 0) {
1623                needSep = true;
1624                pw.println("  Package mode watchers:");
1625                for (int i=0; i<mPackageModeWatchers.size(); i++) {
1626                    pw.print("    Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
1627                    pw.println(":");
1628                    ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
1629                    for (int j=0; j<callbacks.size(); j++) {
1630                        pw.print("      #"); pw.print(j); pw.print(": ");
1631                        pw.println(callbacks.get(j));
1632                    }
1633                }
1634            }
1635            if (mModeWatchers.size() > 0) {
1636                needSep = true;
1637                pw.println("  All mode watchers:");
1638                for (int i=0; i<mModeWatchers.size(); i++) {
1639                    pw.print("    "); pw.print(mModeWatchers.keyAt(i));
1640                    pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
1641                }
1642            }
1643            if (mClients.size() > 0) {
1644                needSep = true;
1645                pw.println("  Clients:");
1646                for (int i=0; i<mClients.size(); i++) {
1647                    pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
1648                    ClientState cs = mClients.valueAt(i);
1649                    pw.print("      "); pw.println(cs);
1650                    if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
1651                        pw.println("      Started ops:");
1652                        for (int j=0; j<cs.mStartedOps.size(); j++) {
1653                            Op op = cs.mStartedOps.get(j);
1654                            pw.print("        "); pw.print("uid="); pw.print(op.uid);
1655                            pw.print(" pkg="); pw.print(op.packageName);
1656                            pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
1657                        }
1658                    }
1659                }
1660            }
1661            if (mAudioRestrictions.size() > 0) {
1662                boolean printedHeader = false;
1663                for (int o=0; o<mAudioRestrictions.size(); o++) {
1664                    final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
1665                    final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o);
1666                    for (int i=0; i<restrictions.size(); i++) {
1667                        if (!printedHeader){
1668                            pw.println("  Audio Restrictions:");
1669                            printedHeader = true;
1670                            needSep = true;
1671                        }
1672                        final int usage = restrictions.keyAt(i);
1673                        pw.print("    "); pw.print(op);
1674                        pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage));
1675                        Restriction r = restrictions.valueAt(i);
1676                        pw.print(": mode="); pw.println(r.mode);
1677                        if (!r.exceptionPackages.isEmpty()) {
1678                            pw.println("      Exceptions:");
1679                            for (int j=0; j<r.exceptionPackages.size(); j++) {
1680                                pw.print("        "); pw.println(r.exceptionPackages.valueAt(j));
1681                            }
1682                        }
1683                    }
1684                }
1685            }
1686            if (needSep) {
1687                pw.println();
1688            }
1689            for (int i=0; i<mUidStates.size(); i++) {
1690                UidState uidState = mUidStates.valueAt(i);
1691
1692                pw.print("  Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
1693
1694                SparseIntArray opModes = uidState.opModes;
1695                if (opModes != null) {
1696                    final int opModeCount = opModes.size();
1697                    for (int j = 0; j < opModeCount; j++) {
1698                        final int code = opModes.keyAt(j);
1699                        final int mode = opModes.valueAt(j);
1700                        pw.print("      "); pw.print(AppOpsManager.opToName(code));
1701                        pw.print(": mode="); pw.println(mode);
1702                    }
1703                }
1704
1705                ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
1706                if (pkgOps == null) {
1707                    continue;
1708                }
1709
1710                for (Ops ops : pkgOps.values()) {
1711                    pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
1712                    for (int j=0; j<ops.size(); j++) {
1713                        Op op = ops.valueAt(j);
1714                        pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
1715                        pw.print(": mode="); pw.print(op.mode);
1716                        if (op.time != 0) {
1717                            pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
1718                            pw.print(" ago");
1719                        }
1720                        if (op.rejectTime != 0) {
1721                            pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
1722                            pw.print(" ago");
1723                        }
1724                        if (op.duration == -1) {
1725                            pw.print(" (running)");
1726                        } else if (op.duration != 0) {
1727                            pw.print("; duration="); TimeUtils.formatDuration(op.duration, pw);
1728                        }
1729                        pw.println();
1730                    }
1731                }
1732            }
1733        }
1734    }
1735
1736    private static final class Restriction {
1737        private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
1738        int mode;
1739        ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
1740    }
1741
1742    @Override
1743    public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException {
1744        checkSystemUid("setUserRestrictions");
1745        boolean[] opRestrictions = mOpRestrictions.get(userHandle);
1746        if (opRestrictions == null) {
1747            opRestrictions = new boolean[AppOpsManager._NUM_OP];
1748            mOpRestrictions.put(userHandle, opRestrictions);
1749        }
1750        for (int i = 0; i < opRestrictions.length; ++i) {
1751            String restriction = AppOpsManager.opToRestriction(i);
1752            if (restriction != null) {
1753                opRestrictions[i] = restrictions.getBoolean(restriction, false);
1754            } else {
1755                opRestrictions[i] = false;
1756            }
1757        }
1758    }
1759
1760    @Override
1761    public void removeUser(int userHandle) throws RemoteException {
1762        checkSystemUid("removeUser");
1763        mOpRestrictions.remove(userHandle);
1764    }
1765
1766    private void checkSystemUid(String function) {
1767        int uid = Binder.getCallingUid();
1768        if (uid != Process.SYSTEM_UID) {
1769            throw new SecurityException(function + " must by called by the system");
1770        }
1771    }
1772
1773    private static String[] getPackagesForUid(int uid) {
1774        String[] packageNames = null;
1775        try {
1776            packageNames= AppGlobals.getPackageManager().getPackagesForUid(uid);
1777        } catch (RemoteException e) {
1778            /* ignore - local call */
1779        }
1780        if (packageNames == null) {
1781            return EmptyArray.STRING;
1782        }
1783        return packageNames;
1784    }
1785}
1786