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