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