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.nio.charset.StandardCharsets;
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 import java.util.Collections;
     30 import java.util.HashMap;
     31 import java.util.Iterator;
     32 import java.util.List;
     33 import java.util.Map;
     34 
     35 import android.Manifest;
     36 import android.app.ActivityManager;
     37 import android.app.ActivityThread;
     38 import android.app.AppGlobals;
     39 import android.app.AppOpsManager;
     40 import android.content.Context;
     41 import android.content.pm.ApplicationInfo;
     42 import android.content.pm.IPackageManager;
     43 import android.content.pm.PackageManager;
     44 import android.media.AudioAttributes;
     45 import android.os.AsyncTask;
     46 import android.os.Binder;
     47 import android.os.Bundle;
     48 import android.os.Handler;
     49 import android.os.IBinder;
     50 import android.os.Process;
     51 import android.os.RemoteException;
     52 import android.os.ResultReceiver;
     53 import android.os.ServiceManager;
     54 import android.os.ShellCommand;
     55 import android.os.UserHandle;
     56 import android.os.storage.MountServiceInternal;
     57 import android.util.ArrayMap;
     58 import android.util.ArraySet;
     59 import android.util.AtomicFile;
     60 import android.util.Log;
     61 import android.util.Slog;
     62 import android.util.SparseArray;
     63 import android.util.SparseIntArray;
     64 import android.util.TimeUtils;
     65 import android.util.Xml;
     66 
     67 import com.android.internal.app.IAppOpsService;
     68 import com.android.internal.app.IAppOpsCallback;
     69 import com.android.internal.os.Zygote;
     70 import com.android.internal.util.ArrayUtils;
     71 import com.android.internal.util.FastXmlSerializer;
     72 import com.android.internal.util.Preconditions;
     73 import com.android.internal.util.XmlUtils;
     74 
     75 import libcore.util.EmptyArray;
     76 import org.xmlpull.v1.XmlPullParser;
     77 import org.xmlpull.v1.XmlPullParserException;
     78 import org.xmlpull.v1.XmlSerializer;
     79 
     80 public class AppOpsService extends IAppOpsService.Stub {
     81     static final String TAG = "AppOps";
     82     static final boolean DEBUG = false;
     83 
     84     // Write at most every 30 minutes.
     85     static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
     86 
     87     Context mContext;
     88     final AtomicFile mFile;
     89     final Handler mHandler;
     90 
     91     boolean mWriteScheduled;
     92     boolean mFastWriteScheduled;
     93     final Runnable mWriteRunner = new Runnable() {
     94         public void run() {
     95             synchronized (AppOpsService.this) {
     96                 mWriteScheduled = false;
     97                 mFastWriteScheduled = false;
     98                 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
     99                     @Override protected Void doInBackground(Void... params) {
    100                         writeState();
    101                         return null;
    102                     }
    103                 };
    104                 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
    105             }
    106         }
    107     };
    108 
    109     private final SparseArray<UidState> mUidStates = new SparseArray<>();
    110 
    111     /*
    112      * These are app op restrictions imposed per user from various parties.
    113      */
    114     private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>();
    115 
    116     private static final class UidState {
    117         public final int uid;
    118         public ArrayMap<String, Ops> pkgOps;
    119         public SparseIntArray opModes;
    120 
    121         public UidState(int uid) {
    122             this.uid = uid;
    123         }
    124 
    125         public void clear() {
    126             pkgOps = null;
    127             opModes = null;
    128         }
    129 
    130         public boolean isDefault() {
    131             return (pkgOps == null || pkgOps.isEmpty())
    132                     && (opModes == null || opModes.size() <= 0);
    133         }
    134     }
    135 
    136     public final static class Ops extends SparseArray<Op> {
    137         public final String packageName;
    138         public final UidState uidState;
    139         public final boolean isPrivileged;
    140 
    141         public Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
    142             packageName = _packageName;
    143             uidState = _uidState;
    144             isPrivileged = _isPrivileged;
    145         }
    146     }
    147 
    148     public final static class Op {
    149         public final int uid;
    150         public final String packageName;
    151         public int proxyUid = -1;
    152         public String proxyPackageName;
    153         public final int op;
    154         public int mode;
    155         public int duration;
    156         public long time;
    157         public long rejectTime;
    158         public int nesting;
    159 
    160         public Op(int _uid, String _packageName, int _op) {
    161             uid = _uid;
    162             packageName = _packageName;
    163             op = _op;
    164             mode = AppOpsManager.opToDefaultMode(op);
    165         }
    166     }
    167 
    168     final SparseArray<ArrayList<Callback>> mOpModeWatchers
    169             = new SparseArray<ArrayList<Callback>>();
    170     final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers
    171             = new ArrayMap<String, ArrayList<Callback>>();
    172     final ArrayMap<IBinder, Callback> mModeWatchers
    173             = new ArrayMap<IBinder, Callback>();
    174     final SparseArray<SparseArray<Restriction>> mAudioRestrictions
    175             = new SparseArray<SparseArray<Restriction>>();
    176 
    177     public final class Callback implements DeathRecipient {
    178         final IAppOpsCallback mCallback;
    179 
    180         public Callback(IAppOpsCallback callback) {
    181             mCallback = callback;
    182             try {
    183                 mCallback.asBinder().linkToDeath(this, 0);
    184             } catch (RemoteException e) {
    185             }
    186         }
    187 
    188         public void unlinkToDeath() {
    189             mCallback.asBinder().unlinkToDeath(this, 0);
    190         }
    191 
    192         @Override
    193         public void binderDied() {
    194             stopWatchingMode(mCallback);
    195         }
    196     }
    197 
    198     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
    199 
    200     public final class ClientState extends Binder implements DeathRecipient {
    201         final IBinder mAppToken;
    202         final int mPid;
    203         final ArrayList<Op> mStartedOps;
    204 
    205         public ClientState(IBinder appToken) {
    206             mAppToken = appToken;
    207             mPid = Binder.getCallingPid();
    208             if (appToken instanceof Binder) {
    209                 // For local clients, there is no reason to track them.
    210                 mStartedOps = null;
    211             } else {
    212                 mStartedOps = new ArrayList<Op>();
    213                 try {
    214                     mAppToken.linkToDeath(this, 0);
    215                 } catch (RemoteException e) {
    216                 }
    217             }
    218         }
    219 
    220         @Override
    221         public String toString() {
    222             return "ClientState{" +
    223                     "mAppToken=" + mAppToken +
    224                     ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
    225                     '}';
    226         }
    227 
    228         @Override
    229         public void binderDied() {
    230             synchronized (AppOpsService.this) {
    231                 for (int i=mStartedOps.size()-1; i>=0; i--) {
    232                     finishOperationLocked(mStartedOps.get(i));
    233                 }
    234                 mClients.remove(mAppToken);
    235             }
    236         }
    237     }
    238 
    239     public AppOpsService(File storagePath, Handler handler) {
    240         mFile = new AtomicFile(storagePath);
    241         mHandler = handler;
    242         readState();
    243     }
    244 
    245     public void publish(Context context) {
    246         mContext = context;
    247         ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
    248     }
    249 
    250     public void systemReady() {
    251         synchronized (this) {
    252             boolean changed = false;
    253             for (int i = mUidStates.size() - 1; i >= 0; i--) {
    254                 UidState uidState = mUidStates.valueAt(i);
    255 
    256                 String[] packageNames = getPackagesForUid(uidState.uid);
    257                 if (ArrayUtils.isEmpty(packageNames)) {
    258                     uidState.clear();
    259                     mUidStates.removeAt(i);
    260                     changed = true;
    261                     continue;
    262                 }
    263 
    264                 ArrayMap<String, Ops> pkgs = uidState.pkgOps;
    265                 if (pkgs == null) {
    266                     continue;
    267                 }
    268 
    269                 Iterator<Ops> it = pkgs.values().iterator();
    270                 while (it.hasNext()) {
    271                     Ops ops = it.next();
    272                     int curUid = -1;
    273                     try {
    274                         curUid = AppGlobals.getPackageManager().getPackageUid(ops.packageName,
    275                                 PackageManager.MATCH_UNINSTALLED_PACKAGES,
    276                                 UserHandle.getUserId(ops.uidState.uid));
    277                     } catch (RemoteException ignored) {
    278                     }
    279                     if (curUid != ops.uidState.uid) {
    280                         Slog.i(TAG, "Pruning old package " + ops.packageName
    281                                 + "/" + ops.uidState + ": new uid=" + curUid);
    282                         it.remove();
    283                         changed = true;
    284                     }
    285                 }
    286 
    287                 if (uidState.isDefault()) {
    288                     mUidStates.removeAt(i);
    289                 }
    290             }
    291             if (changed) {
    292                 scheduleFastWriteLocked();
    293             }
    294         }
    295 
    296         MountServiceInternal mountServiceInternal = LocalServices.getService(
    297                 MountServiceInternal.class);
    298         mountServiceInternal.addExternalStoragePolicy(
    299                 new MountServiceInternal.ExternalStorageMountPolicy() {
    300                     @Override
    301                     public int getMountMode(int uid, String packageName) {
    302                         if (Process.isIsolated(uid)) {
    303                             return Zygote.MOUNT_EXTERNAL_NONE;
    304                         }
    305                         if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
    306                                 packageName) != AppOpsManager.MODE_ALLOWED) {
    307                             return Zygote.MOUNT_EXTERNAL_NONE;
    308                         }
    309                         if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
    310                                 packageName) != AppOpsManager.MODE_ALLOWED) {
    311                             return Zygote.MOUNT_EXTERNAL_READ;
    312                         }
    313                         return Zygote.MOUNT_EXTERNAL_WRITE;
    314                     }
    315 
    316                     @Override
    317                     public boolean hasExternalStorage(int uid, String packageName) {
    318                         final int mountMode = getMountMode(uid, packageName);
    319                         return mountMode == Zygote.MOUNT_EXTERNAL_READ
    320                                 || mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
    321                     }
    322                 });
    323     }
    324 
    325     public void packageRemoved(int uid, String packageName) {
    326         synchronized (this) {
    327             UidState uidState = mUidStates.get(uid);
    328             if (uidState == null) {
    329                 return;
    330             }
    331 
    332             boolean changed = false;
    333 
    334             // Remove any package state if such.
    335             if (uidState.pkgOps != null && uidState.pkgOps.remove(packageName) != null) {
    336                 changed = true;
    337             }
    338 
    339             // If we just nuked the last package state check if the UID is valid.
    340             if (changed && uidState.pkgOps.isEmpty()
    341                     && getPackagesForUid(uid).length <= 0) {
    342                 mUidStates.remove(uid);
    343             }
    344 
    345             if (changed) {
    346                 scheduleFastWriteLocked();
    347             }
    348         }
    349     }
    350 
    351     public void uidRemoved(int uid) {
    352         synchronized (this) {
    353             if (mUidStates.indexOfKey(uid) >= 0) {
    354                 mUidStates.remove(uid);
    355                 scheduleFastWriteLocked();
    356             }
    357         }
    358     }
    359 
    360     public void shutdown() {
    361         Slog.w(TAG, "Writing app ops before shutdown...");
    362         boolean doWrite = false;
    363         synchronized (this) {
    364             if (mWriteScheduled) {
    365                 mWriteScheduled = false;
    366                 doWrite = true;
    367             }
    368         }
    369         if (doWrite) {
    370             writeState();
    371         }
    372     }
    373 
    374     private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
    375         ArrayList<AppOpsManager.OpEntry> resOps = null;
    376         if (ops == null) {
    377             resOps = new ArrayList<AppOpsManager.OpEntry>();
    378             for (int j=0; j<pkgOps.size(); j++) {
    379                 Op curOp = pkgOps.valueAt(j);
    380                 resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
    381                         curOp.rejectTime, curOp.duration, curOp.proxyUid,
    382                         curOp.proxyPackageName));
    383             }
    384         } else {
    385             for (int j=0; j<ops.length; j++) {
    386                 Op curOp = pkgOps.get(ops[j]);
    387                 if (curOp != null) {
    388                     if (resOps == null) {
    389                         resOps = new ArrayList<AppOpsManager.OpEntry>();
    390                     }
    391                     resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
    392                             curOp.rejectTime, curOp.duration, curOp.proxyUid,
    393                             curOp.proxyPackageName));
    394                 }
    395             }
    396         }
    397         return resOps;
    398     }
    399 
    400     @Override
    401     public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
    402         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
    403                 Binder.getCallingPid(), Binder.getCallingUid(), null);
    404         ArrayList<AppOpsManager.PackageOps> res = null;
    405         synchronized (this) {
    406             final int uidStateCount = mUidStates.size();
    407             for (int i = 0; i < uidStateCount; i++) {
    408                 UidState uidState = mUidStates.valueAt(i);
    409                 if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()) {
    410                     continue;
    411                 }
    412                 ArrayMap<String, Ops> packages = uidState.pkgOps;
    413                 final int packageCount = packages.size();
    414                 for (int j = 0; j < packageCount; j++) {
    415                     Ops pkgOps = packages.valueAt(j);
    416                     ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
    417                     if (resOps != null) {
    418                         if (res == null) {
    419                             res = new ArrayList<AppOpsManager.PackageOps>();
    420                         }
    421                         AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
    422                                 pkgOps.packageName, pkgOps.uidState.uid, resOps);
    423                         res.add(resPackage);
    424                     }
    425                 }
    426             }
    427         }
    428         return res;
    429     }
    430 
    431     @Override
    432     public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
    433             int[] ops) {
    434         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
    435                 Binder.getCallingPid(), Binder.getCallingUid(), null);
    436         String resolvedPackageName = resolvePackageName(uid, packageName);
    437         if (resolvedPackageName == null) {
    438             return Collections.emptyList();
    439         }
    440         synchronized (this) {
    441             Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false);
    442             if (pkgOps == null) {
    443                 return null;
    444             }
    445             ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
    446             if (resOps == null) {
    447                 return null;
    448             }
    449             ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
    450             AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
    451                     pkgOps.packageName, pkgOps.uidState.uid, resOps);
    452             res.add(resPackage);
    453             return res;
    454         }
    455     }
    456 
    457     private void pruneOp(Op op, int uid, String packageName) {
    458         if (op.time == 0 && op.rejectTime == 0) {
    459             Ops ops = getOpsRawLocked(uid, packageName, false);
    460             if (ops != null) {
    461                 ops.remove(op.op);
    462                 if (ops.size() <= 0) {
    463                     UidState uidState = ops.uidState;
    464                     ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
    465                     if (pkgOps != null) {
    466                         pkgOps.remove(ops.packageName);
    467                         if (pkgOps.isEmpty()) {
    468                             uidState.pkgOps = null;
    469                         }
    470                         if (uidState.isDefault()) {
    471                             mUidStates.remove(uid);
    472                         }
    473                     }
    474                 }
    475             }
    476         }
    477     }
    478 
    479     @Override
    480     public void setUidMode(int code, int uid, int mode) {
    481         if (Binder.getCallingPid() != Process.myPid()) {
    482             mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
    483                     Binder.getCallingPid(), Binder.getCallingUid(), null);
    484         }
    485         verifyIncomingOp(code);
    486         code = AppOpsManager.opToSwitch(code);
    487 
    488         synchronized (this) {
    489             final int defaultMode = AppOpsManager.opToDefaultMode(code);
    490 
    491             UidState uidState = getUidStateLocked(uid, false);
    492             if (uidState == null) {
    493                 if (mode == defaultMode) {
    494                     return;
    495                 }
    496                 uidState = new UidState(uid);
    497                 uidState.opModes = new SparseIntArray();
    498                 uidState.opModes.put(code, mode);
    499                 mUidStates.put(uid, uidState);
    500                 scheduleWriteLocked();
    501             } else if (uidState.opModes == null) {
    502                 if (mode != defaultMode) {
    503                     uidState.opModes = new SparseIntArray();
    504                     uidState.opModes.put(code, mode);
    505                     scheduleWriteLocked();
    506                 }
    507             } else {
    508                 if (uidState.opModes.get(code) == mode) {
    509                     return;
    510                 }
    511                 if (mode == defaultMode) {
    512                     uidState.opModes.delete(code);
    513                     if (uidState.opModes.size() <= 0) {
    514                         uidState.opModes = null;
    515                     }
    516                 } else {
    517                     uidState.opModes.put(code, mode);
    518                 }
    519                 scheduleWriteLocked();
    520             }
    521         }
    522 
    523         String[] uidPackageNames = getPackagesForUid(uid);
    524         ArrayMap<Callback, ArraySet<String>> callbackSpecs = null;
    525 
    526         synchronized (this) {
    527             ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
    528             if (callbacks != null) {
    529                 final int callbackCount = callbacks.size();
    530                 for (int i = 0; i < callbackCount; i++) {
    531                     Callback callback = callbacks.get(i);
    532                     ArraySet<String> changedPackages = new ArraySet<>();
    533                     Collections.addAll(changedPackages, uidPackageNames);
    534                     callbackSpecs = new ArrayMap<>();
    535                     callbackSpecs.put(callback, changedPackages);
    536                 }
    537             }
    538 
    539             for (String uidPackageName : uidPackageNames) {
    540                 callbacks = mPackageModeWatchers.get(uidPackageName);
    541                 if (callbacks != null) {
    542                     if (callbackSpecs == null) {
    543                         callbackSpecs = new ArrayMap<>();
    544                     }
    545                     final int callbackCount = callbacks.size();
    546                     for (int i = 0; i < callbackCount; i++) {
    547                         Callback callback = callbacks.get(i);
    548                         ArraySet<String> changedPackages = callbackSpecs.get(callback);
    549                         if (changedPackages == null) {
    550                             changedPackages = new ArraySet<>();
    551                             callbackSpecs.put(callback, changedPackages);
    552                         }
    553                         changedPackages.add(uidPackageName);
    554                     }
    555                 }
    556             }
    557         }
    558 
    559         if (callbackSpecs == null) {
    560             return;
    561         }
    562 
    563         // There are components watching for mode changes such as window manager
    564         // and location manager which are in our process. The callbacks in these
    565         // components may require permissions our remote caller does not have.
    566         final long identity = Binder.clearCallingIdentity();
    567         try {
    568             for (int i = 0; i < callbackSpecs.size(); i++) {
    569                 Callback callback = callbackSpecs.keyAt(i);
    570                 ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
    571                 try {
    572                     if (reportedPackageNames == null) {
    573                         callback.mCallback.opChanged(code, uid, null);
    574                     } else {
    575                         final int reportedPackageCount = reportedPackageNames.size();
    576                         for (int j = 0; j < reportedPackageCount; j++) {
    577                             String reportedPackageName = reportedPackageNames.valueAt(j);
    578                             callback.mCallback.opChanged(code, uid, reportedPackageName);
    579                         }
    580                     }
    581                 } catch (RemoteException e) {
    582                     Log.w(TAG, "Error dispatching op op change", e);
    583                 }
    584             }
    585         } finally {
    586             Binder.restoreCallingIdentity(identity);
    587         }
    588     }
    589 
    590     @Override
    591     public void setMode(int code, int uid, String packageName, int mode) {
    592         if (Binder.getCallingPid() != Process.myPid()) {
    593             mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
    594                     Binder.getCallingPid(), Binder.getCallingUid(), null);
    595         }
    596         verifyIncomingOp(code);
    597         ArrayList<Callback> repCbs = null;
    598         code = AppOpsManager.opToSwitch(code);
    599         synchronized (this) {
    600             UidState uidState = getUidStateLocked(uid, false);
    601             Op op = getOpLocked(code, uid, packageName, true);
    602             if (op != null) {
    603                 if (op.mode != mode) {
    604                     op.mode = mode;
    605                     ArrayList<Callback> cbs = mOpModeWatchers.get(code);
    606                     if (cbs != null) {
    607                         if (repCbs == null) {
    608                             repCbs = new ArrayList<Callback>();
    609                         }
    610                         repCbs.addAll(cbs);
    611                     }
    612                     cbs = mPackageModeWatchers.get(packageName);
    613                     if (cbs != null) {
    614                         if (repCbs == null) {
    615                             repCbs = new ArrayList<Callback>();
    616                         }
    617                         repCbs.addAll(cbs);
    618                     }
    619                     if (mode == AppOpsManager.opToDefaultMode(op.op)) {
    620                         // If going into the default mode, prune this op
    621                         // if there is nothing else interesting in it.
    622                         pruneOp(op, uid, packageName);
    623                     }
    624                     scheduleFastWriteLocked();
    625                 }
    626             }
    627         }
    628         if (repCbs != null) {
    629             // There are components watching for mode changes such as window manager
    630             // and location manager which are in our process. The callbacks in these
    631             // components may require permissions our remote caller does not have.
    632             final long identity = Binder.clearCallingIdentity();
    633             try {
    634                 for (int i = 0; i < repCbs.size(); i++) {
    635                     try {
    636                         repCbs.get(i).mCallback.opChanged(code, uid, packageName);
    637                     } catch (RemoteException e) {
    638                     }
    639                 }
    640             } finally {
    641                 Binder.restoreCallingIdentity(identity);
    642             }
    643         }
    644     }
    645 
    646     private static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks(
    647             HashMap<Callback, ArrayList<ChangeRec>> callbacks,
    648             int op, int uid, String packageName, ArrayList<Callback> cbs) {
    649         if (cbs == null) {
    650             return callbacks;
    651         }
    652         if (callbacks == null) {
    653             callbacks = new HashMap<>();
    654         }
    655         boolean duplicate = false;
    656         for (int i=0; i<cbs.size(); i++) {
    657             Callback cb = cbs.get(i);
    658             ArrayList<ChangeRec> reports = callbacks.get(cb);
    659             if (reports == null) {
    660                 reports = new ArrayList<>();
    661                 callbacks.put(cb, reports);
    662             } else {
    663                 final int reportCount = reports.size();
    664                 for (int j = 0; j < reportCount; j++) {
    665                     ChangeRec report = reports.get(j);
    666                     if (report.op == op && report.pkg.equals(packageName)) {
    667                         duplicate = true;
    668                         break;
    669                     }
    670                 }
    671             }
    672             if (!duplicate) {
    673                 reports.add(new ChangeRec(op, uid, packageName));
    674             }
    675         }
    676         return callbacks;
    677     }
    678 
    679     static final class ChangeRec {
    680         final int op;
    681         final int uid;
    682         final String pkg;
    683 
    684         ChangeRec(int _op, int _uid, String _pkg) {
    685             op = _op;
    686             uid = _uid;
    687             pkg = _pkg;
    688         }
    689     }
    690 
    691     @Override
    692     public void resetAllModes(int reqUserId, String reqPackageName) {
    693         final int callingPid = Binder.getCallingPid();
    694         final int callingUid = Binder.getCallingUid();
    695         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
    696                 callingPid, callingUid, null);
    697         reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId,
    698                 true, true, "resetAllModes", null);
    699 
    700         int reqUid = -1;
    701         if (reqPackageName != null) {
    702             try {
    703                 reqUid = AppGlobals.getPackageManager().getPackageUid(
    704                         reqPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, reqUserId);
    705             } catch (RemoteException e) {
    706                 /* ignore - local call */
    707             }
    708         }
    709 
    710         HashMap<Callback, ArrayList<ChangeRec>> callbacks = null;
    711         synchronized (this) {
    712             boolean changed = false;
    713             for (int i = mUidStates.size() - 1; i >= 0; i--) {
    714                 UidState uidState = mUidStates.valueAt(i);
    715 
    716                 SparseIntArray opModes = uidState.opModes;
    717                 if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
    718                     final int uidOpCount = opModes.size();
    719                     for (int j = uidOpCount - 1; j >= 0; j--) {
    720                         final int code = opModes.keyAt(j);
    721                         if (AppOpsManager.opAllowsReset(code)) {
    722                             opModes.removeAt(j);
    723                             if (opModes.size() <= 0) {
    724                                 uidState.opModes = null;
    725                             }
    726                             for (String packageName : getPackagesForUid(uidState.uid)) {
    727                                 callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
    728                                         mOpModeWatchers.get(code));
    729                                 callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
    730                                         mPackageModeWatchers.get(packageName));
    731                             }
    732                         }
    733                     }
    734                 }
    735 
    736                 if (uidState.pkgOps == null) {
    737                     continue;
    738                 }
    739 
    740                 if (reqUserId != UserHandle.USER_ALL
    741                         && reqUserId != UserHandle.getUserId(uidState.uid)) {
    742                     // Skip any ops for a different user
    743                     continue;
    744                 }
    745 
    746                 Map<String, Ops> packages = uidState.pkgOps;
    747                 Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
    748                 while (it.hasNext()) {
    749                     Map.Entry<String, Ops> ent = it.next();
    750                     String packageName = ent.getKey();
    751                     if (reqPackageName != null && !reqPackageName.equals(packageName)) {
    752                         // Skip any ops for a different package
    753                         continue;
    754                     }
    755                     Ops pkgOps = ent.getValue();
    756                     for (int j=pkgOps.size()-1; j>=0; j--) {
    757                         Op curOp = pkgOps.valueAt(j);
    758                         if (AppOpsManager.opAllowsReset(curOp.op)
    759                                 && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
    760                             curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
    761                             changed = true;
    762                             callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
    763                                     mOpModeWatchers.get(curOp.op));
    764                             callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
    765                                     mPackageModeWatchers.get(packageName));
    766                             if (curOp.time == 0 && curOp.rejectTime == 0) {
    767                                 pkgOps.removeAt(j);
    768                             }
    769                         }
    770                     }
    771                     if (pkgOps.size() == 0) {
    772                         it.remove();
    773                     }
    774                 }
    775                 if (uidState.isDefault()) {
    776                     mUidStates.remove(uidState.uid);
    777                 }
    778             }
    779 
    780             if (changed) {
    781                 scheduleFastWriteLocked();
    782             }
    783         }
    784         if (callbacks != null) {
    785             for (Map.Entry<Callback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
    786                 Callback cb = ent.getKey();
    787                 ArrayList<ChangeRec> reports = ent.getValue();
    788                 for (int i=0; i<reports.size(); i++) {
    789                     ChangeRec rep = reports.get(i);
    790                     try {
    791                         cb.mCallback.opChanged(rep.op, rep.uid, rep.pkg);
    792                     } catch (RemoteException e) {
    793                     }
    794                 }
    795             }
    796         }
    797     }
    798 
    799     @Override
    800     public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
    801         if (callback == null) {
    802             return;
    803         }
    804         synchronized (this) {
    805             op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
    806             Callback cb = mModeWatchers.get(callback.asBinder());
    807             if (cb == null) {
    808                 cb = new Callback(callback);
    809                 mModeWatchers.put(callback.asBinder(), cb);
    810             }
    811             if (op != AppOpsManager.OP_NONE) {
    812                 ArrayList<Callback> cbs = mOpModeWatchers.get(op);
    813                 if (cbs == null) {
    814                     cbs = new ArrayList<Callback>();
    815                     mOpModeWatchers.put(op, cbs);
    816                 }
    817                 cbs.add(cb);
    818             }
    819             if (packageName != null) {
    820                 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
    821                 if (cbs == null) {
    822                     cbs = new ArrayList<Callback>();
    823                     mPackageModeWatchers.put(packageName, cbs);
    824                 }
    825                 cbs.add(cb);
    826             }
    827         }
    828     }
    829 
    830     @Override
    831     public void stopWatchingMode(IAppOpsCallback callback) {
    832         if (callback == null) {
    833             return;
    834         }
    835         synchronized (this) {
    836             Callback cb = mModeWatchers.remove(callback.asBinder());
    837             if (cb != null) {
    838                 cb.unlinkToDeath();
    839                 for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
    840                     ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
    841                     cbs.remove(cb);
    842                     if (cbs.size() <= 0) {
    843                         mOpModeWatchers.removeAt(i);
    844                     }
    845                 }
    846                 for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
    847                     ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
    848                     cbs.remove(cb);
    849                     if (cbs.size() <= 0) {
    850                         mPackageModeWatchers.removeAt(i);
    851                     }
    852                 }
    853             }
    854         }
    855     }
    856 
    857     @Override
    858     public IBinder getToken(IBinder clientToken) {
    859         synchronized (this) {
    860             ClientState cs = mClients.get(clientToken);
    861             if (cs == null) {
    862                 cs = new ClientState(clientToken);
    863                 mClients.put(clientToken, cs);
    864             }
    865             return cs;
    866         }
    867     }
    868 
    869     @Override
    870     public int checkOperation(int code, int uid, String packageName) {
    871         verifyIncomingUid(uid);
    872         verifyIncomingOp(code);
    873         String resolvedPackageName = resolvePackageName(uid, packageName);
    874         if (resolvedPackageName == null) {
    875             return AppOpsManager.MODE_IGNORED;
    876         }
    877         synchronized (this) {
    878             if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
    879                 return AppOpsManager.MODE_IGNORED;
    880             }
    881             code = AppOpsManager.opToSwitch(code);
    882             UidState uidState = getUidStateLocked(uid, false);
    883             if (uidState != null && uidState.opModes != null) {
    884                 final int uidMode = uidState.opModes.get(code);
    885                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
    886                     return uidMode;
    887                 }
    888             }
    889             Op op = getOpLocked(code, uid, resolvedPackageName, false);
    890             if (op == null) {
    891                 return AppOpsManager.opToDefaultMode(code);
    892             }
    893             return op.mode;
    894         }
    895     }
    896 
    897     @Override
    898     public int checkAudioOperation(int code, int usage, int uid, String packageName) {
    899         boolean suspended;
    900         try {
    901             suspended = isPackageSuspendedForUser(packageName, uid);
    902         } catch (IllegalArgumentException ex) {
    903             // Package not found.
    904             suspended = false;
    905         }
    906 
    907         if (suspended) {
    908             Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
    909             return AppOpsManager.MODE_IGNORED;
    910         }
    911 
    912         synchronized (this) {
    913             final int mode = checkRestrictionLocked(code, usage, uid, packageName);
    914             if (mode != AppOpsManager.MODE_ALLOWED) {
    915                 return mode;
    916             }
    917         }
    918         return checkOperation(code, uid, packageName);
    919     }
    920 
    921     private boolean isPackageSuspendedForUser(String pkg, int uid) {
    922         try {
    923             return AppGlobals.getPackageManager().isPackageSuspendedForUser(
    924                     pkg, UserHandle.getUserId(uid));
    925         } catch (RemoteException re) {
    926             throw new SecurityException("Could not talk to package manager service");
    927         }
    928     }
    929 
    930     private int checkRestrictionLocked(int code, int usage, int uid, String packageName) {
    931         final SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
    932         if (usageRestrictions != null) {
    933             final Restriction r = usageRestrictions.get(usage);
    934             if (r != null && !r.exceptionPackages.contains(packageName)) {
    935                 return r.mode;
    936             }
    937         }
    938         return AppOpsManager.MODE_ALLOWED;
    939     }
    940 
    941     @Override
    942     public void setAudioRestriction(int code, int usage, int uid, int mode,
    943             String[] exceptionPackages) {
    944         verifyIncomingUid(uid);
    945         verifyIncomingOp(code);
    946         synchronized (this) {
    947             SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
    948             if (usageRestrictions == null) {
    949                 usageRestrictions = new SparseArray<Restriction>();
    950                 mAudioRestrictions.put(code, usageRestrictions);
    951             }
    952             usageRestrictions.remove(usage);
    953             if (mode != AppOpsManager.MODE_ALLOWED) {
    954                 final Restriction r = new Restriction();
    955                 r.mode = mode;
    956                 if (exceptionPackages != null) {
    957                     final int N = exceptionPackages.length;
    958                     r.exceptionPackages = new ArraySet<String>(N);
    959                     for (int i = 0; i < N; i++) {
    960                         final String pkg = exceptionPackages[i];
    961                         if (pkg != null) {
    962                             r.exceptionPackages.add(pkg.trim());
    963                         }
    964                     }
    965                 }
    966                 usageRestrictions.put(usage, r);
    967             }
    968         }
    969         notifyWatchersOfChange(code);
    970     }
    971 
    972     @Override
    973     public int checkPackage(int uid, String packageName) {
    974         Preconditions.checkNotNull(packageName);
    975         synchronized (this) {
    976             if (getOpsRawLocked(uid, packageName, true) != null) {
    977                 return AppOpsManager.MODE_ALLOWED;
    978             } else {
    979                 return AppOpsManager.MODE_ERRORED;
    980             }
    981         }
    982     }
    983 
    984     @Override
    985     public int noteProxyOperation(int code, String proxyPackageName,
    986             int proxiedUid, String proxiedPackageName) {
    987         verifyIncomingOp(code);
    988         final int proxyUid = Binder.getCallingUid();
    989         String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
    990         if (resolveProxyPackageName == null) {
    991             return AppOpsManager.MODE_IGNORED;
    992         }
    993         final int proxyMode = noteOperationUnchecked(code, proxyUid,
    994                 resolveProxyPackageName, -1, null);
    995         if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
    996             return proxyMode;
    997         }
    998         String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
    999         if (resolveProxiedPackageName == null) {
   1000             return AppOpsManager.MODE_IGNORED;
   1001         }
   1002         return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
   1003                 proxyMode, resolveProxyPackageName);
   1004     }
   1005 
   1006     @Override
   1007     public int noteOperation(int code, int uid, String packageName) {
   1008         verifyIncomingUid(uid);
   1009         verifyIncomingOp(code);
   1010         String resolvedPackageName = resolvePackageName(uid, packageName);
   1011         if (resolvedPackageName == null) {
   1012             return AppOpsManager.MODE_IGNORED;
   1013         }
   1014         return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
   1015     }
   1016 
   1017     private int noteOperationUnchecked(int code, int uid, String packageName,
   1018             int proxyUid, String proxyPackageName) {
   1019         synchronized (this) {
   1020             Ops ops = getOpsRawLocked(uid, packageName, true);
   1021             if (ops == null) {
   1022                 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
   1023                         + " package " + packageName);
   1024                 return AppOpsManager.MODE_ERRORED;
   1025             }
   1026             Op op = getOpLocked(ops, code, true);
   1027             if (isOpRestrictedLocked(uid, code, packageName)) {
   1028                 return AppOpsManager.MODE_IGNORED;
   1029             }
   1030             if (op.duration == -1) {
   1031                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
   1032                         + " code " + code + " time=" + op.time + " duration=" + op.duration);
   1033             }
   1034             op.duration = 0;
   1035             final int switchCode = AppOpsManager.opToSwitch(code);
   1036             UidState uidState = ops.uidState;
   1037             // If there is a non-default per UID policy (we set UID op mode only if
   1038             // non-default) it takes over, otherwise use the per package policy.
   1039             if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
   1040                 final int uidMode = uidState.opModes.get(switchCode);
   1041                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
   1042                     if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
   1043                             + switchCode + " (" + code + ") uid " + uid + " package "
   1044                             + packageName);
   1045                     op.rejectTime = System.currentTimeMillis();
   1046                     return uidMode;
   1047                 }
   1048             } else {
   1049                 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
   1050                 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
   1051                     if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
   1052                             + switchCode + " (" + code + ") uid " + uid + " package "
   1053                             + packageName);
   1054                     op.rejectTime = System.currentTimeMillis();
   1055                     return switchOp.mode;
   1056                 }
   1057             }
   1058             if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
   1059                     + " package " + packageName);
   1060             op.time = System.currentTimeMillis();
   1061             op.rejectTime = 0;
   1062             op.proxyUid = proxyUid;
   1063             op.proxyPackageName = proxyPackageName;
   1064             return AppOpsManager.MODE_ALLOWED;
   1065         }
   1066     }
   1067 
   1068     @Override
   1069     public int startOperation(IBinder token, int code, int uid, String packageName) {
   1070         verifyIncomingUid(uid);
   1071         verifyIncomingOp(code);
   1072         String resolvedPackageName = resolvePackageName(uid, packageName);
   1073         if (resolvedPackageName == null) {
   1074             return  AppOpsManager.MODE_IGNORED;
   1075         }
   1076         ClientState client = (ClientState)token;
   1077         synchronized (this) {
   1078             Ops ops = getOpsRawLocked(uid, resolvedPackageName, true);
   1079             if (ops == null) {
   1080                 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
   1081                         + " package " + resolvedPackageName);
   1082                 return AppOpsManager.MODE_ERRORED;
   1083             }
   1084             Op op = getOpLocked(ops, code, true);
   1085             if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
   1086                 return AppOpsManager.MODE_IGNORED;
   1087             }
   1088             final int switchCode = AppOpsManager.opToSwitch(code);
   1089             UidState uidState = ops.uidState;
   1090             if (uidState.opModes != null) {
   1091                 final int uidMode = uidState.opModes.get(switchCode);
   1092                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
   1093                     if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
   1094                             + switchCode + " (" + code + ") uid " + uid + " package "
   1095                             + resolvedPackageName);
   1096                     op.rejectTime = System.currentTimeMillis();
   1097                     return uidMode;
   1098                 }
   1099             }
   1100             final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
   1101             if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
   1102                 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
   1103                         + switchCode + " (" + code + ") uid " + uid + " package "
   1104                         + resolvedPackageName);
   1105                 op.rejectTime = System.currentTimeMillis();
   1106                 return switchOp.mode;
   1107             }
   1108             if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
   1109                     + " package " + resolvedPackageName);
   1110             if (op.nesting == 0) {
   1111                 op.time = System.currentTimeMillis();
   1112                 op.rejectTime = 0;
   1113                 op.duration = -1;
   1114             }
   1115             op.nesting++;
   1116             if (client.mStartedOps != null) {
   1117                 client.mStartedOps.add(op);
   1118             }
   1119             return AppOpsManager.MODE_ALLOWED;
   1120         }
   1121     }
   1122 
   1123     @Override
   1124     public void finishOperation(IBinder token, int code, int uid, String packageName) {
   1125         verifyIncomingUid(uid);
   1126         verifyIncomingOp(code);
   1127         String resolvedPackageName = resolvePackageName(uid, packageName);
   1128         if (resolvedPackageName == null) {
   1129             return;
   1130         }
   1131         if (!(token instanceof ClientState)) {
   1132             return;
   1133         }
   1134         ClientState client = (ClientState) token;
   1135         synchronized (this) {
   1136             Op op = getOpLocked(code, uid, resolvedPackageName, true);
   1137             if (op == null) {
   1138                 return;
   1139             }
   1140             if (client.mStartedOps != null) {
   1141                 if (!client.mStartedOps.remove(op)) {
   1142                     throw new IllegalStateException("Operation not started: uid" + op.uid
   1143                             + " pkg=" + op.packageName + " op=" + op.op);
   1144                 }
   1145             }
   1146             finishOperationLocked(op);
   1147         }
   1148     }
   1149 
   1150     @Override
   1151     public int permissionToOpCode(String permission) {
   1152         if (permission == null) {
   1153             return AppOpsManager.OP_NONE;
   1154         }
   1155         return AppOpsManager.permissionToOpCode(permission);
   1156     }
   1157 
   1158     void finishOperationLocked(Op op) {
   1159         if (op.nesting <= 1) {
   1160             if (op.nesting == 1) {
   1161                 op.duration = (int)(System.currentTimeMillis() - op.time);
   1162                 op.time += op.duration;
   1163             } else {
   1164                 Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
   1165                         + op.packageName + " code " + op.op + " time=" + op.time
   1166                         + " duration=" + op.duration + " nesting=" + op.nesting);
   1167             }
   1168             op.nesting = 0;
   1169         } else {
   1170             op.nesting--;
   1171         }
   1172     }
   1173 
   1174     private void verifyIncomingUid(int uid) {
   1175         if (uid == Binder.getCallingUid()) {
   1176             return;
   1177         }
   1178         if (Binder.getCallingPid() == Process.myPid()) {
   1179             return;
   1180         }
   1181         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
   1182                 Binder.getCallingPid(), Binder.getCallingUid(), null);
   1183     }
   1184 
   1185     private void verifyIncomingOp(int op) {
   1186         if (op >= 0 && op < AppOpsManager._NUM_OP) {
   1187             return;
   1188         }
   1189         throw new IllegalArgumentException("Bad operation #" + op);
   1190     }
   1191 
   1192     private UidState getUidStateLocked(int uid, boolean edit) {
   1193         UidState uidState = mUidStates.get(uid);
   1194         if (uidState == null) {
   1195             if (!edit) {
   1196                 return null;
   1197             }
   1198             uidState = new UidState(uid);
   1199             mUidStates.put(uid, uidState);
   1200         }
   1201         return uidState;
   1202     }
   1203 
   1204     private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
   1205         UidState uidState = getUidStateLocked(uid, edit);
   1206         if (uidState == null) {
   1207             return null;
   1208         }
   1209 
   1210         if (uidState.pkgOps == null) {
   1211             if (!edit) {
   1212                 return null;
   1213             }
   1214             uidState.pkgOps = new ArrayMap<>();
   1215         }
   1216 
   1217         Ops ops = uidState.pkgOps.get(packageName);
   1218         if (ops == null) {
   1219             if (!edit) {
   1220                 return null;
   1221             }
   1222             boolean isPrivileged = false;
   1223             // This is the first time we have seen this package name under this uid,
   1224             // so let's make sure it is valid.
   1225             if (uid != 0) {
   1226                 final long ident = Binder.clearCallingIdentity();
   1227                 try {
   1228                     int pkgUid = -1;
   1229                     try {
   1230                         ApplicationInfo appInfo = ActivityThread.getPackageManager()
   1231                                 .getApplicationInfo(packageName,
   1232                                         PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   1233                                         UserHandle.getUserId(uid));
   1234                         if (appInfo != null) {
   1235                             pkgUid = appInfo.uid;
   1236                             isPrivileged = (appInfo.privateFlags
   1237                                     & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
   1238                         } else {
   1239                             if ("media".equals(packageName)) {
   1240                                 pkgUid = Process.MEDIA_UID;
   1241                                 isPrivileged = false;
   1242                             } else if ("audioserver".equals(packageName)) {
   1243                                 pkgUid = Process.AUDIOSERVER_UID;
   1244                                 isPrivileged = false;
   1245                             } else if ("cameraserver".equals(packageName)) {
   1246                                 pkgUid = Process.CAMERASERVER_UID;
   1247                                 isPrivileged = false;
   1248                             }
   1249                         }
   1250                     } catch (RemoteException e) {
   1251                         Slog.w(TAG, "Could not contact PackageManager", e);
   1252                     }
   1253                     if (pkgUid != uid) {
   1254                         // Oops!  The package name is not valid for the uid they are calling
   1255                         // under.  Abort.
   1256                         RuntimeException ex = new RuntimeException("here");
   1257                         ex.fillInStackTrace();
   1258                         Slog.w(TAG, "Bad call: specified package " + packageName
   1259                                 + " under uid " + uid + " but it is really " + pkgUid, ex);
   1260                         return null;
   1261                     }
   1262                 } finally {
   1263                     Binder.restoreCallingIdentity(ident);
   1264                 }
   1265             }
   1266             ops = new Ops(packageName, uidState, isPrivileged);
   1267             uidState.pkgOps.put(packageName, ops);
   1268         }
   1269         return ops;
   1270     }
   1271 
   1272     private void scheduleWriteLocked() {
   1273         if (!mWriteScheduled) {
   1274             mWriteScheduled = true;
   1275             mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
   1276         }
   1277     }
   1278 
   1279     private void scheduleFastWriteLocked() {
   1280         if (!mFastWriteScheduled) {
   1281             mWriteScheduled = true;
   1282             mFastWriteScheduled = true;
   1283             mHandler.removeCallbacks(mWriteRunner);
   1284             mHandler.postDelayed(mWriteRunner, 10*1000);
   1285         }
   1286     }
   1287 
   1288     private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
   1289         Ops ops = getOpsRawLocked(uid, packageName, edit);
   1290         if (ops == null) {
   1291             return null;
   1292         }
   1293         return getOpLocked(ops, code, edit);
   1294     }
   1295 
   1296     private Op getOpLocked(Ops ops, int code, boolean edit) {
   1297         Op op = ops.get(code);
   1298         if (op == null) {
   1299             if (!edit) {
   1300                 return null;
   1301             }
   1302             op = new Op(ops.uidState.uid, ops.packageName, code);
   1303             ops.put(code, op);
   1304         }
   1305         if (edit) {
   1306             scheduleWriteLocked();
   1307         }
   1308         return op;
   1309     }
   1310 
   1311     private boolean isOpRestrictedLocked(int uid, int code, String packageName) {
   1312         int userHandle = UserHandle.getUserId(uid);
   1313         final int restrictionSetCount = mOpUserRestrictions.size();
   1314 
   1315         for (int i = 0; i < restrictionSetCount; i++) {
   1316             // For each client, check that the given op is not restricted, or that the given
   1317             // package is exempt from the restriction.
   1318             ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
   1319             if (restrictionState.hasRestriction(code, packageName, userHandle)) {
   1320                 if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
   1321                     // If we are the system, bypass user restrictions for certain codes
   1322                     synchronized (this) {
   1323                         Ops ops = getOpsRawLocked(uid, packageName, true);
   1324                         if ((ops != null) && ops.isPrivileged) {
   1325                             return false;
   1326                         }
   1327                     }
   1328                 }
   1329                 return true;
   1330             }
   1331         }
   1332         return false;
   1333     }
   1334 
   1335     void readState() {
   1336         synchronized (mFile) {
   1337             synchronized (this) {
   1338                 FileInputStream stream;
   1339                 try {
   1340                     stream = mFile.openRead();
   1341                 } catch (FileNotFoundException e) {
   1342                     Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
   1343                     return;
   1344                 }
   1345                 boolean success = false;
   1346                 mUidStates.clear();
   1347                 try {
   1348                     XmlPullParser parser = Xml.newPullParser();
   1349                     parser.setInput(stream, StandardCharsets.UTF_8.name());
   1350                     int type;
   1351                     while ((type = parser.next()) != XmlPullParser.START_TAG
   1352                             && type != XmlPullParser.END_DOCUMENT) {
   1353                         ;
   1354                     }
   1355 
   1356                     if (type != XmlPullParser.START_TAG) {
   1357                         throw new IllegalStateException("no start tag found");
   1358                     }
   1359 
   1360                     int outerDepth = parser.getDepth();
   1361                     while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
   1362                             && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   1363                         if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1364                             continue;
   1365                         }
   1366 
   1367                         String tagName = parser.getName();
   1368                         if (tagName.equals("pkg")) {
   1369                             readPackage(parser);
   1370                         } else if (tagName.equals("uid")) {
   1371                             readUidOps(parser);
   1372                         } else {
   1373                             Slog.w(TAG, "Unknown element under <app-ops>: "
   1374                                     + parser.getName());
   1375                             XmlUtils.skipCurrentTag(parser);
   1376                         }
   1377                     }
   1378                     success = true;
   1379                 } catch (IllegalStateException e) {
   1380                     Slog.w(TAG, "Failed parsing " + e);
   1381                 } catch (NullPointerException e) {
   1382                     Slog.w(TAG, "Failed parsing " + e);
   1383                 } catch (NumberFormatException e) {
   1384                     Slog.w(TAG, "Failed parsing " + e);
   1385                 } catch (XmlPullParserException e) {
   1386                     Slog.w(TAG, "Failed parsing " + e);
   1387                 } catch (IOException e) {
   1388                     Slog.w(TAG, "Failed parsing " + e);
   1389                 } catch (IndexOutOfBoundsException e) {
   1390                     Slog.w(TAG, "Failed parsing " + e);
   1391                 } finally {
   1392                     if (!success) {
   1393                         mUidStates.clear();
   1394                     }
   1395                     try {
   1396                         stream.close();
   1397                     } catch (IOException e) {
   1398                     }
   1399                 }
   1400             }
   1401         }
   1402     }
   1403 
   1404     void readUidOps(XmlPullParser parser) throws NumberFormatException,
   1405             XmlPullParserException, IOException {
   1406         final int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
   1407         int outerDepth = parser.getDepth();
   1408         int type;
   1409         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
   1410                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   1411             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1412                 continue;
   1413             }
   1414 
   1415             String tagName = parser.getName();
   1416             if (tagName.equals("op")) {
   1417                 final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
   1418                 final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
   1419                 UidState uidState = getUidStateLocked(uid, true);
   1420                 if (uidState.opModes == null) {
   1421                     uidState.opModes = new SparseIntArray();
   1422                 }
   1423                 uidState.opModes.put(code, mode);
   1424             } else {
   1425                 Slog.w(TAG, "Unknown element under <uid-ops>: "
   1426                         + parser.getName());
   1427                 XmlUtils.skipCurrentTag(parser);
   1428             }
   1429         }
   1430     }
   1431 
   1432     void readPackage(XmlPullParser parser) throws NumberFormatException,
   1433             XmlPullParserException, IOException {
   1434         String pkgName = parser.getAttributeValue(null, "n");
   1435         int outerDepth = parser.getDepth();
   1436         int type;
   1437         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
   1438                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   1439             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1440                 continue;
   1441             }
   1442 
   1443             String tagName = parser.getName();
   1444             if (tagName.equals("uid")) {
   1445                 readUid(parser, pkgName);
   1446             } else {
   1447                 Slog.w(TAG, "Unknown element under <pkg>: "
   1448                         + parser.getName());
   1449                 XmlUtils.skipCurrentTag(parser);
   1450             }
   1451         }
   1452     }
   1453 
   1454     void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
   1455             XmlPullParserException, IOException {
   1456         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
   1457         String isPrivilegedString = parser.getAttributeValue(null, "p");
   1458         boolean isPrivileged = false;
   1459         if (isPrivilegedString == null) {
   1460             try {
   1461                 IPackageManager packageManager = ActivityThread.getPackageManager();
   1462                 if (packageManager != null) {
   1463                     ApplicationInfo appInfo = ActivityThread.getPackageManager()
   1464                             .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
   1465                     if (appInfo != null) {
   1466                         isPrivileged = (appInfo.privateFlags
   1467                                 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
   1468                     }
   1469                 } else {
   1470                     // Could not load data, don't add to cache so it will be loaded later.
   1471                     return;
   1472                 }
   1473             } catch (RemoteException e) {
   1474                 Slog.w(TAG, "Could not contact PackageManager", e);
   1475             }
   1476         } else {
   1477             isPrivileged = Boolean.parseBoolean(isPrivilegedString);
   1478         }
   1479         int outerDepth = parser.getDepth();
   1480         int type;
   1481         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
   1482                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   1483             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1484                 continue;
   1485             }
   1486 
   1487             String tagName = parser.getName();
   1488             if (tagName.equals("op")) {
   1489                 Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
   1490                 String mode = parser.getAttributeValue(null, "m");
   1491                 if (mode != null) {
   1492                     op.mode = Integer.parseInt(mode);
   1493                 }
   1494                 String time = parser.getAttributeValue(null, "t");
   1495                 if (time != null) {
   1496                     op.time = Long.parseLong(time);
   1497                 }
   1498                 time = parser.getAttributeValue(null, "r");
   1499                 if (time != null) {
   1500                     op.rejectTime = Long.parseLong(time);
   1501                 }
   1502                 String dur = parser.getAttributeValue(null, "d");
   1503                 if (dur != null) {
   1504                     op.duration = Integer.parseInt(dur);
   1505                 }
   1506                 String proxyUid = parser.getAttributeValue(null, "pu");
   1507                 if (proxyUid != null) {
   1508                     op.proxyUid = Integer.parseInt(proxyUid);
   1509                 }
   1510                 String proxyPackageName = parser.getAttributeValue(null, "pp");
   1511                 if (proxyPackageName != null) {
   1512                     op.proxyPackageName = proxyPackageName;
   1513                 }
   1514 
   1515                 UidState uidState = getUidStateLocked(uid, true);
   1516                 if (uidState.pkgOps == null) {
   1517                     uidState.pkgOps = new ArrayMap<>();
   1518                 }
   1519 
   1520                 Ops ops = uidState.pkgOps.get(pkgName);
   1521                 if (ops == null) {
   1522                     ops = new Ops(pkgName, uidState, isPrivileged);
   1523                     uidState.pkgOps.put(pkgName, ops);
   1524                 }
   1525                 ops.put(op.op, op);
   1526             } else {
   1527                 Slog.w(TAG, "Unknown element under <pkg>: "
   1528                         + parser.getName());
   1529                 XmlUtils.skipCurrentTag(parser);
   1530             }
   1531         }
   1532     }
   1533 
   1534     void writeState() {
   1535         synchronized (mFile) {
   1536             List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
   1537 
   1538             FileOutputStream stream;
   1539             try {
   1540                 stream = mFile.startWrite();
   1541             } catch (IOException e) {
   1542                 Slog.w(TAG, "Failed to write state: " + e);
   1543                 return;
   1544             }
   1545 
   1546             try {
   1547                 XmlSerializer out = new FastXmlSerializer();
   1548                 out.setOutput(stream, StandardCharsets.UTF_8.name());
   1549                 out.startDocument(null, true);
   1550                 out.startTag(null, "app-ops");
   1551 
   1552                 final int uidStateCount = mUidStates.size();
   1553                 for (int i = 0; i < uidStateCount; i++) {
   1554                     UidState uidState = mUidStates.valueAt(i);
   1555                     if (uidState.opModes != null && uidState.opModes.size() > 0) {
   1556                         out.startTag(null, "uid");
   1557                         out.attribute(null, "n", Integer.toString(uidState.uid));
   1558                         SparseIntArray uidOpModes = uidState.opModes;
   1559                         final int opCount = uidOpModes.size();
   1560                         for (int j = 0; j < opCount; j++) {
   1561                             final int op = uidOpModes.keyAt(j);
   1562                             final int mode = uidOpModes.valueAt(j);
   1563                             out.startTag(null, "op");
   1564                             out.attribute(null, "n", Integer.toString(op));
   1565                             out.attribute(null, "m", Integer.toString(mode));
   1566                             out.endTag(null, "op");
   1567                         }
   1568                         out.endTag(null, "uid");
   1569                     }
   1570                 }
   1571 
   1572                 if (allOps != null) {
   1573                     String lastPkg = null;
   1574                     for (int i=0; i<allOps.size(); i++) {
   1575                         AppOpsManager.PackageOps pkg = allOps.get(i);
   1576                         if (!pkg.getPackageName().equals(lastPkg)) {
   1577                             if (lastPkg != null) {
   1578                                 out.endTag(null, "pkg");
   1579                             }
   1580                             lastPkg = pkg.getPackageName();
   1581                             out.startTag(null, "pkg");
   1582                             out.attribute(null, "n", lastPkg);
   1583                         }
   1584                         out.startTag(null, "uid");
   1585                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
   1586                         synchronized (this) {
   1587                             Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false);
   1588                             // Should always be present as the list of PackageOps is generated
   1589                             // from Ops.
   1590                             if (ops != null) {
   1591                                 out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
   1592                             } else {
   1593                                 out.attribute(null, "p", Boolean.toString(false));
   1594                             }
   1595                         }
   1596                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
   1597                         for (int j=0; j<ops.size(); j++) {
   1598                             AppOpsManager.OpEntry op = ops.get(j);
   1599                             out.startTag(null, "op");
   1600                             out.attribute(null, "n", Integer.toString(op.getOp()));
   1601                             if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
   1602                                 out.attribute(null, "m", Integer.toString(op.getMode()));
   1603                             }
   1604                             long time = op.getTime();
   1605                             if (time != 0) {
   1606                                 out.attribute(null, "t", Long.toString(time));
   1607                             }
   1608                             time = op.getRejectTime();
   1609                             if (time != 0) {
   1610                                 out.attribute(null, "r", Long.toString(time));
   1611                             }
   1612                             int dur = op.getDuration();
   1613                             if (dur != 0) {
   1614                                 out.attribute(null, "d", Integer.toString(dur));
   1615                             }
   1616                             int proxyUid = op.getProxyUid();
   1617                             if (proxyUid != -1) {
   1618                                 out.attribute(null, "pu", Integer.toString(proxyUid));
   1619                             }
   1620                             String proxyPackageName = op.getProxyPackageName();
   1621                             if (proxyPackageName != null) {
   1622                                 out.attribute(null, "pp", proxyPackageName);
   1623                             }
   1624                             out.endTag(null, "op");
   1625                         }
   1626                         out.endTag(null, "uid");
   1627                     }
   1628                     if (lastPkg != null) {
   1629                         out.endTag(null, "pkg");
   1630                     }
   1631                 }
   1632 
   1633                 out.endTag(null, "app-ops");
   1634                 out.endDocument();
   1635                 mFile.finishWrite(stream);
   1636             } catch (IOException e) {
   1637                 Slog.w(TAG, "Failed to write state, restoring backup.", e);
   1638                 mFile.failWrite(stream);
   1639             }
   1640         }
   1641     }
   1642 
   1643     static class Shell extends ShellCommand {
   1644         final IAppOpsService mInterface;
   1645         final AppOpsService mInternal;
   1646 
   1647         int userId = UserHandle.USER_SYSTEM;
   1648         String packageName;
   1649         String opStr;
   1650         String modeStr;
   1651         int op;
   1652         int mode;
   1653         int packageUid;
   1654 
   1655         Shell(IAppOpsService iface, AppOpsService internal) {
   1656             mInterface = iface;
   1657             mInternal = internal;
   1658         }
   1659 
   1660         @Override
   1661         public int onCommand(String cmd) {
   1662             return onShellCommand(this, cmd);
   1663         }
   1664 
   1665         @Override
   1666         public void onHelp() {
   1667             PrintWriter pw = getOutPrintWriter();
   1668             dumpCommandHelp(pw);
   1669         }
   1670 
   1671         private int strOpToOp(String op, PrintWriter err) {
   1672             try {
   1673                 return AppOpsManager.strOpToOp(op);
   1674             } catch (IllegalArgumentException e) {
   1675             }
   1676             try {
   1677                 return Integer.parseInt(op);
   1678             } catch (NumberFormatException e) {
   1679             }
   1680             try {
   1681                 return AppOpsManager.strDebugOpToOp(op);
   1682             } catch (IllegalArgumentException e) {
   1683                 err.println("Error: " + e.getMessage());
   1684                 return -1;
   1685             }
   1686         }
   1687 
   1688         int strModeToMode(String modeStr, PrintWriter err) {
   1689             switch (modeStr) {
   1690                 case "allow":
   1691                     return AppOpsManager.MODE_ALLOWED;
   1692                 case "deny":
   1693                     return AppOpsManager.MODE_ERRORED;
   1694                 case "ignore":
   1695                     return AppOpsManager.MODE_IGNORED;
   1696                 case "default":
   1697                     return AppOpsManager.MODE_DEFAULT;
   1698             }
   1699             try {
   1700                 return Integer.parseInt(modeStr);
   1701             } catch (NumberFormatException e) {
   1702             }
   1703             err.println("Error: Mode " + modeStr + " is not valid");
   1704             return -1;
   1705         }
   1706 
   1707         int parseUserOpMode(int defMode, PrintWriter err) throws RemoteException {
   1708             userId = UserHandle.USER_CURRENT;
   1709             opStr = null;
   1710             modeStr = null;
   1711             for (String argument; (argument = getNextArg()) != null;) {
   1712                 if ("--user".equals(argument)) {
   1713                     userId = UserHandle.parseUserArg(getNextArgRequired());
   1714                 } else {
   1715                     if (opStr == null) {
   1716                         opStr = argument;
   1717                     } else if (modeStr == null) {
   1718                         modeStr = argument;
   1719                         break;
   1720                     }
   1721                 }
   1722             }
   1723             if (opStr == null) {
   1724                 err.println("Error: Operation not specified.");
   1725                 return -1;
   1726             }
   1727             op = strOpToOp(opStr, err);
   1728             if (op < 0) {
   1729                 return -1;
   1730             }
   1731             if (modeStr != null) {
   1732                 if ((mode=strModeToMode(modeStr, err)) < 0) {
   1733                     return -1;
   1734                 }
   1735             } else {
   1736                 mode = defMode;
   1737             }
   1738             return 0;
   1739         }
   1740 
   1741         int parseUserPackageOp(boolean reqOp, PrintWriter err) throws RemoteException {
   1742             userId = UserHandle.USER_CURRENT;
   1743             packageName = null;
   1744             opStr = null;
   1745             for (String argument; (argument = getNextArg()) != null;) {
   1746                 if ("--user".equals(argument)) {
   1747                     userId = UserHandle.parseUserArg(getNextArgRequired());
   1748                 } else {
   1749                     if (packageName == null) {
   1750                         packageName = argument;
   1751                     } else if (opStr == null) {
   1752                         opStr = argument;
   1753                         break;
   1754                     }
   1755                 }
   1756             }
   1757             if (packageName == null) {
   1758                 err.println("Error: Package name not specified.");
   1759                 return -1;
   1760             } else if (opStr == null && reqOp) {
   1761                 err.println("Error: Operation not specified.");
   1762                 return -1;
   1763             }
   1764             if (opStr != null) {
   1765                 op = strOpToOp(opStr, err);
   1766                 if (op < 0) {
   1767                     return -1;
   1768                 }
   1769             } else {
   1770                 op = AppOpsManager.OP_NONE;
   1771             }
   1772             if (userId == UserHandle.USER_CURRENT) {
   1773                 userId = ActivityManager.getCurrentUser();
   1774             }
   1775             if ("root".equals(packageName)) {
   1776                 packageUid = 0;
   1777             } else {
   1778                 packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
   1779                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
   1780             }
   1781             if (packageUid < 0) {
   1782                 err.println("Error: No UID for " + packageName + " in user " + userId);
   1783                 return -1;
   1784             }
   1785             return 0;
   1786         }
   1787     }
   1788 
   1789     @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
   1790             FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
   1791         (new Shell(this, this)).exec(this, in, out, err, args, resultReceiver);
   1792     }
   1793 
   1794     static void dumpCommandHelp(PrintWriter pw) {
   1795         pw.println("AppOps service (appops) commands:");
   1796         pw.println("  help");
   1797         pw.println("    Print this help text.");
   1798         pw.println("  set [--user <USER_ID>] <PACKAGE> <OP> <MODE>");
   1799         pw.println("    Set the mode for a particular application and operation.");
   1800         pw.println("  get [--user <USER_ID>] <PACKAGE> [<OP>]");
   1801         pw.println("    Return the mode for a particular application and optional operation.");
   1802         pw.println("  query-op [--user <USER_ID>] <OP> [<MODE>]");
   1803         pw.println("    Print all packages that currently have the given op in the given mode.");
   1804         pw.println("  reset [--user <USER_ID>] [<PACKAGE>]");
   1805         pw.println("    Reset the given application or all applications to default modes.");
   1806         pw.println("  write-settings");
   1807         pw.println("    Immediately write pending changes to storage.");
   1808         pw.println("  read-settings");
   1809         pw.println("    Read the last written settings, replacing current state in RAM.");
   1810         pw.println("  options:");
   1811         pw.println("    <PACKAGE> an Android package name.");
   1812         pw.println("    <OP>      an AppOps operation.");
   1813         pw.println("    <MODE>    one of allow, ignore, deny, or default");
   1814         pw.println("    <USER_ID> the user id under which the package is installed. If --user is not");
   1815         pw.println("              specified, the current user is assumed.");
   1816     }
   1817 
   1818     static int onShellCommand(Shell shell, String cmd) {
   1819         if (cmd == null) {
   1820             return shell.handleDefaultCommands(cmd);
   1821         }
   1822         PrintWriter pw = shell.getOutPrintWriter();
   1823         PrintWriter err = shell.getErrPrintWriter();
   1824         try {
   1825             switch (cmd) {
   1826                 case "set": {
   1827                     int res = shell.parseUserPackageOp(true, err);
   1828                     if (res < 0) {
   1829                         return res;
   1830                     }
   1831                     String modeStr = shell.getNextArg();
   1832                     if (modeStr == null) {
   1833                         err.println("Error: Mode not specified.");
   1834                         return -1;
   1835                     }
   1836 
   1837                     final int mode = shell.strModeToMode(modeStr, err);
   1838                     if (mode < 0) {
   1839                         return -1;
   1840                     }
   1841 
   1842                     shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode);
   1843                     return 0;
   1844                 }
   1845                 case "get": {
   1846                     int res = shell.parseUserPackageOp(false, err);
   1847                     if (res < 0) {
   1848                         return res;
   1849                     }
   1850 
   1851                     List<AppOpsManager.PackageOps> ops = shell.mInterface.getOpsForPackage(
   1852                             shell.packageUid, shell.packageName,
   1853                             shell.op != AppOpsManager.OP_NONE ? new int[] {shell.op} : null);
   1854                     if (ops == null || ops.size() <= 0) {
   1855                         pw.println("No operations.");
   1856                         return 0;
   1857                     }
   1858                     final long now = System.currentTimeMillis();
   1859                     for (int i=0; i<ops.size(); i++) {
   1860                         List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
   1861                         for (int j=0; j<entries.size(); j++) {
   1862                             AppOpsManager.OpEntry ent = entries.get(j);
   1863                             pw.print(AppOpsManager.opToName(ent.getOp()));
   1864                             pw.print(": ");
   1865                             switch (ent.getMode()) {
   1866                                 case AppOpsManager.MODE_ALLOWED:
   1867                                     pw.print("allow");
   1868                                     break;
   1869                                 case AppOpsManager.MODE_IGNORED:
   1870                                     pw.print("ignore");
   1871                                     break;
   1872                                 case AppOpsManager.MODE_ERRORED:
   1873                                     pw.print("deny");
   1874                                     break;
   1875                                 case AppOpsManager.MODE_DEFAULT:
   1876                                     pw.print("default");
   1877                                     break;
   1878                                 default:
   1879                                     pw.print("mode=");
   1880                                     pw.print(ent.getMode());
   1881                                     break;
   1882                             }
   1883                             if (ent.getTime() != 0) {
   1884                                 pw.print("; time=");
   1885                                 TimeUtils.formatDuration(now - ent.getTime(), pw);
   1886                                 pw.print(" ago");
   1887                             }
   1888                             if (ent.getRejectTime() != 0) {
   1889                                 pw.print("; rejectTime=");
   1890                                 TimeUtils.formatDuration(now - ent.getRejectTime(), pw);
   1891                                 pw.print(" ago");
   1892                             }
   1893                             if (ent.getDuration() == -1) {
   1894                                 pw.print(" (running)");
   1895                             } else if (ent.getDuration() != 0) {
   1896                                 pw.print("; duration=");
   1897                                 TimeUtils.formatDuration(ent.getDuration(), pw);
   1898                             }
   1899                             pw.println();
   1900                         }
   1901                     }
   1902                     return 0;
   1903                 }
   1904                 case "query-op": {
   1905                     int res = shell.parseUserOpMode(AppOpsManager.MODE_IGNORED, err);
   1906                     if (res < 0) {
   1907                         return res;
   1908                     }
   1909                     List<AppOpsManager.PackageOps> ops = shell.mInterface.getPackagesForOps(
   1910                             new int[] {shell.op});
   1911                     if (ops == null || ops.size() <= 0) {
   1912                         pw.println("No operations.");
   1913                         return 0;
   1914                     }
   1915                     for (int i=0; i<ops.size(); i++) {
   1916                         final AppOpsManager.PackageOps pkg = ops.get(i);
   1917                         boolean hasMatch = false;
   1918                         final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
   1919                         for (int j=0; j<entries.size(); j++) {
   1920                             AppOpsManager.OpEntry ent = entries.get(j);
   1921                             if (ent.getOp() == shell.op && ent.getMode() == shell.mode) {
   1922                                 hasMatch = true;
   1923                                 break;
   1924                             }
   1925                         }
   1926                         if (hasMatch) {
   1927                             pw.println(pkg.getPackageName());
   1928                         }
   1929                     }
   1930                     return 0;
   1931                 }
   1932                 case "reset": {
   1933                     String packageName = null;
   1934                     int userId = UserHandle.USER_CURRENT;
   1935                     for (String argument; (argument = shell.getNextArg()) != null;) {
   1936                         if ("--user".equals(argument)) {
   1937                             String userStr = shell.getNextArgRequired();
   1938                             userId = UserHandle.parseUserArg(userStr);
   1939                         } else {
   1940                             if (packageName == null) {
   1941                                 packageName = argument;
   1942                             } else {
   1943                                 err.println("Error: Unsupported argument: " + argument);
   1944                                 return -1;
   1945                             }
   1946                         }
   1947                     }
   1948 
   1949                     if (userId == UserHandle.USER_CURRENT) {
   1950                         userId = ActivityManager.getCurrentUser();
   1951                     }
   1952 
   1953                     shell.mInterface.resetAllModes(userId, packageName);
   1954                     pw.print("Reset all modes for: ");
   1955                     if (userId == UserHandle.USER_ALL) {
   1956                         pw.print("all users");
   1957                     } else {
   1958                         pw.print("user "); pw.print(userId);
   1959                     }
   1960                     pw.print(", ");
   1961                     if (packageName == null) {
   1962                         pw.println("all packages");
   1963                     } else {
   1964                         pw.print("package "); pw.println(packageName);
   1965                     }
   1966                     return 0;
   1967                 }
   1968                 case "write-settings": {
   1969                     shell.mInternal.mContext.enforcePermission(
   1970                             android.Manifest.permission.UPDATE_APP_OPS_STATS,
   1971                             Binder.getCallingPid(), Binder.getCallingUid(), null);
   1972                     long token = Binder.clearCallingIdentity();
   1973                     try {
   1974                         synchronized (shell.mInternal) {
   1975                             shell.mInternal.mHandler.removeCallbacks(shell.mInternal.mWriteRunner);
   1976                         }
   1977                         shell.mInternal.writeState();
   1978                         pw.println("Current settings written.");
   1979                     } finally {
   1980                         Binder.restoreCallingIdentity(token);
   1981                     }
   1982                     return 0;
   1983                 }
   1984                 case "read-settings": {
   1985                     shell.mInternal.mContext.enforcePermission(
   1986                             android.Manifest.permission.UPDATE_APP_OPS_STATS,
   1987                             Binder.getCallingPid(), Binder.getCallingUid(), null);
   1988                     long token = Binder.clearCallingIdentity();
   1989                     try {
   1990                         shell.mInternal.readState();
   1991                         pw.println("Last settings read.");
   1992                     } finally {
   1993                         Binder.restoreCallingIdentity(token);
   1994                     }
   1995                     return 0;
   1996                 }
   1997                 default:
   1998                     return shell.handleDefaultCommands(cmd);
   1999             }
   2000         } catch (RemoteException e) {
   2001             pw.println("Remote exception: " + e);
   2002         }
   2003         return -1;
   2004     }
   2005 
   2006     private void dumpHelp(PrintWriter pw) {
   2007         pw.println("AppOps service (appops) dump options:");
   2008         pw.println("  none");
   2009     }
   2010 
   2011     @Override
   2012     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   2013         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   2014                 != PackageManager.PERMISSION_GRANTED) {
   2015             pw.println("Permission Denial: can't dump ApOps service from from pid="
   2016                     + Binder.getCallingPid()
   2017                     + ", uid=" + Binder.getCallingUid());
   2018             return;
   2019         }
   2020 
   2021         if (args != null) {
   2022             for (int i=0; i<args.length; i++) {
   2023                 String arg = args[i];
   2024                 if ("-h".equals(arg)) {
   2025                     dumpHelp(pw);
   2026                     return;
   2027                 } else if ("-a".equals(arg)) {
   2028                     // dump all data
   2029                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
   2030                     pw.println("Unknown option: " + arg);
   2031                     return;
   2032                 } else {
   2033                     pw.println("Unknown command: " + arg);
   2034                     return;
   2035                 }
   2036             }
   2037         }
   2038 
   2039         synchronized (this) {
   2040             pw.println("Current AppOps Service state:");
   2041             final long now = System.currentTimeMillis();
   2042             boolean needSep = false;
   2043             if (mOpModeWatchers.size() > 0) {
   2044                 needSep = true;
   2045                 pw.println("  Op mode watchers:");
   2046                 for (int i=0; i<mOpModeWatchers.size(); i++) {
   2047                     pw.print("    Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
   2048                     pw.println(":");
   2049                     ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
   2050                     for (int j=0; j<callbacks.size(); j++) {
   2051                         pw.print("      #"); pw.print(j); pw.print(": ");
   2052                         pw.println(callbacks.get(j));
   2053                     }
   2054                 }
   2055             }
   2056             if (mPackageModeWatchers.size() > 0) {
   2057                 needSep = true;
   2058                 pw.println("  Package mode watchers:");
   2059                 for (int i=0; i<mPackageModeWatchers.size(); i++) {
   2060                     pw.print("    Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
   2061                     pw.println(":");
   2062                     ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
   2063                     for (int j=0; j<callbacks.size(); j++) {
   2064                         pw.print("      #"); pw.print(j); pw.print(": ");
   2065                         pw.println(callbacks.get(j));
   2066                     }
   2067                 }
   2068             }
   2069             if (mModeWatchers.size() > 0) {
   2070                 needSep = true;
   2071                 pw.println("  All mode watchers:");
   2072                 for (int i=0; i<mModeWatchers.size(); i++) {
   2073                     pw.print("    "); pw.print(mModeWatchers.keyAt(i));
   2074                     pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
   2075                 }
   2076             }
   2077             if (mClients.size() > 0) {
   2078                 needSep = true;
   2079                 pw.println("  Clients:");
   2080                 for (int i=0; i<mClients.size(); i++) {
   2081                     pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
   2082                     ClientState cs = mClients.valueAt(i);
   2083                     pw.print("      "); pw.println(cs);
   2084                     if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
   2085                         pw.println("      Started ops:");
   2086                         for (int j=0; j<cs.mStartedOps.size(); j++) {
   2087                             Op op = cs.mStartedOps.get(j);
   2088                             pw.print("        "); pw.print("uid="); pw.print(op.uid);
   2089                             pw.print(" pkg="); pw.print(op.packageName);
   2090                             pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
   2091                         }
   2092                     }
   2093                 }
   2094             }
   2095             if (mAudioRestrictions.size() > 0) {
   2096                 boolean printedHeader = false;
   2097                 for (int o=0; o<mAudioRestrictions.size(); o++) {
   2098                     final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
   2099                     final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o);
   2100                     for (int i=0; i<restrictions.size(); i++) {
   2101                         if (!printedHeader){
   2102                             pw.println("  Audio Restrictions:");
   2103                             printedHeader = true;
   2104                             needSep = true;
   2105                         }
   2106                         final int usage = restrictions.keyAt(i);
   2107                         pw.print("    "); pw.print(op);
   2108                         pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage));
   2109                         Restriction r = restrictions.valueAt(i);
   2110                         pw.print(": mode="); pw.println(r.mode);
   2111                         if (!r.exceptionPackages.isEmpty()) {
   2112                             pw.println("      Exceptions:");
   2113                             for (int j=0; j<r.exceptionPackages.size(); j++) {
   2114                                 pw.print("        "); pw.println(r.exceptionPackages.valueAt(j));
   2115                             }
   2116                         }
   2117                     }
   2118                 }
   2119             }
   2120             if (needSep) {
   2121                 pw.println();
   2122             }
   2123             for (int i=0; i<mUidStates.size(); i++) {
   2124                 UidState uidState = mUidStates.valueAt(i);
   2125 
   2126                 pw.print("  Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
   2127 
   2128                 SparseIntArray opModes = uidState.opModes;
   2129                 if (opModes != null) {
   2130                     final int opModeCount = opModes.size();
   2131                     for (int j = 0; j < opModeCount; j++) {
   2132                         final int code = opModes.keyAt(j);
   2133                         final int mode = opModes.valueAt(j);
   2134                         pw.print("      "); pw.print(AppOpsManager.opToName(code));
   2135                         pw.print(": mode="); pw.println(mode);
   2136                     }
   2137                 }
   2138 
   2139                 ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
   2140                 if (pkgOps == null) {
   2141                     continue;
   2142                 }
   2143 
   2144                 for (Ops ops : pkgOps.values()) {
   2145                     pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
   2146                     for (int j=0; j<ops.size(); j++) {
   2147                         Op op = ops.valueAt(j);
   2148                         pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
   2149                         pw.print(": mode="); pw.print(op.mode);
   2150                         if (op.time != 0) {
   2151                             pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
   2152                             pw.print(" ago");
   2153                         }
   2154                         if (op.rejectTime != 0) {
   2155                             pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
   2156                             pw.print(" ago");
   2157                         }
   2158                         if (op.duration == -1) {
   2159                             pw.print(" (running)");
   2160                         } else if (op.duration != 0) {
   2161                             pw.print("; duration="); TimeUtils.formatDuration(op.duration, pw);
   2162                         }
   2163                         pw.println();
   2164                     }
   2165                 }
   2166             }
   2167         }
   2168     }
   2169 
   2170     private static final class Restriction {
   2171         private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
   2172         int mode;
   2173         ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
   2174     }
   2175 
   2176     @Override
   2177     public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
   2178         checkSystemUid("setUserRestrictions");
   2179         Preconditions.checkNotNull(restrictions);
   2180         Preconditions.checkNotNull(token);
   2181         for (int i = 0; i < AppOpsManager._NUM_OP; i++) {
   2182             String restriction = AppOpsManager.opToRestriction(i);
   2183             if (restriction != null) {
   2184                 setUserRestrictionNoCheck(i, restrictions.getBoolean(restriction, false), token,
   2185                         userHandle, null);
   2186             }
   2187         }
   2188     }
   2189 
   2190     @Override
   2191     public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
   2192             String[] exceptionPackages) {
   2193         if (Binder.getCallingPid() != Process.myPid()) {
   2194             mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
   2195                     Binder.getCallingPid(), Binder.getCallingUid(), null);
   2196         }
   2197         if (userHandle != UserHandle.getCallingUserId()) {
   2198             if (mContext.checkCallingOrSelfPermission(Manifest.permission
   2199                     .INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED
   2200                 && mContext.checkCallingOrSelfPermission(Manifest.permission
   2201                     .INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) {
   2202                 throw new SecurityException("Need INTERACT_ACROSS_USERS_FULL or"
   2203                         + " INTERACT_ACROSS_USERS to interact cross user ");
   2204             }
   2205         }
   2206         verifyIncomingOp(code);
   2207         Preconditions.checkNotNull(token);
   2208         setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages);
   2209     }
   2210 
   2211     private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
   2212             int userHandle, String[] exceptionPackages) {
   2213         boolean notifyChange = false;
   2214 
   2215         synchronized (AppOpsService.this) {
   2216             ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
   2217 
   2218             if (restrictionState == null) {
   2219                 try {
   2220                     restrictionState = new ClientRestrictionState(token);
   2221                 } catch (RemoteException e) {
   2222                     return;
   2223                 }
   2224                 mOpUserRestrictions.put(token, restrictionState);
   2225             }
   2226 
   2227             if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
   2228                 notifyChange = true;
   2229             }
   2230 
   2231             if (restrictionState.isDefault()) {
   2232                 mOpUserRestrictions.remove(token);
   2233                 restrictionState.destroy();
   2234             }
   2235         }
   2236 
   2237         if (notifyChange) {
   2238             notifyWatchersOfChange(code);
   2239         }
   2240     }
   2241 
   2242     private void notifyWatchersOfChange(int code) {
   2243         final ArrayList<Callback> clonedCallbacks;
   2244         synchronized (this) {
   2245             ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
   2246             if (callbacks == null) {
   2247                 return;
   2248             }
   2249             clonedCallbacks = new ArrayList<>(callbacks);
   2250         }
   2251 
   2252         // There are components watching for mode changes such as window manager
   2253         // and location manager which are in our process. The callbacks in these
   2254         // components may require permissions our remote caller does not have.s
   2255         final long identity = Binder.clearCallingIdentity();
   2256         try {
   2257             final int callbackCount = clonedCallbacks.size();
   2258             for (int i = 0; i < callbackCount; i++) {
   2259                 Callback callback = clonedCallbacks.get(i);
   2260                 try {
   2261                     callback.mCallback.opChanged(code, -1, null);
   2262                 } catch (RemoteException e) {
   2263                     Log.w(TAG, "Error dispatching op op change", e);
   2264                 }
   2265             }
   2266         } finally {
   2267             Binder.restoreCallingIdentity(identity);
   2268         }
   2269     }
   2270 
   2271     @Override
   2272     public void removeUser(int userHandle) throws RemoteException {
   2273         checkSystemUid("removeUser");
   2274         synchronized (AppOpsService.this) {
   2275             final int tokenCount = mOpUserRestrictions.size();
   2276             for (int i = tokenCount - 1; i >= 0; i--) {
   2277                 ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
   2278                 opRestrictions.removeUser(userHandle);
   2279             }
   2280         }
   2281     }
   2282 
   2283     private void checkSystemUid(String function) {
   2284         int uid = Binder.getCallingUid();
   2285         if (uid != Process.SYSTEM_UID) {
   2286             throw new SecurityException(function + " must by called by the system");
   2287         }
   2288     }
   2289 
   2290     private static String resolvePackageName(int uid, String packageName)  {
   2291         if (uid == 0) {
   2292             return "root";
   2293         } else if (uid == Process.SHELL_UID) {
   2294             return "com.android.shell";
   2295         } else if (uid == Process.SYSTEM_UID && packageName == null) {
   2296             return "android";
   2297         }
   2298         return packageName;
   2299     }
   2300 
   2301     private static String[] getPackagesForUid(int uid) {
   2302         String[] packageNames = null;
   2303         try {
   2304             packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
   2305         } catch (RemoteException e) {
   2306             /* ignore - local call */
   2307         }
   2308         if (packageNames == null) {
   2309             return EmptyArray.STRING;
   2310         }
   2311         return packageNames;
   2312     }
   2313 
   2314     private final class ClientRestrictionState implements DeathRecipient {
   2315         private final IBinder token;
   2316         SparseArray<boolean[]> perUserRestrictions;
   2317         SparseArray<String[]> perUserExcludedPackages;
   2318 
   2319         public ClientRestrictionState(IBinder token)
   2320                 throws RemoteException {
   2321             token.linkToDeath(this, 0);
   2322             this.token = token;
   2323         }
   2324 
   2325         public boolean setRestriction(int code, boolean restricted,
   2326                 String[] excludedPackages, int userId) {
   2327             boolean changed = false;
   2328 
   2329             if (perUserRestrictions == null && restricted) {
   2330                 perUserRestrictions = new SparseArray<>();
   2331             }
   2332 
   2333             if (perUserRestrictions != null) {
   2334                 boolean[] userRestrictions = perUserRestrictions.get(userId);
   2335                 if (userRestrictions == null && restricted) {
   2336                     userRestrictions = new boolean[AppOpsManager._NUM_OP];
   2337                     perUserRestrictions.put(userId, userRestrictions);
   2338                 }
   2339                 if (userRestrictions != null && userRestrictions[code] != restricted) {
   2340                     userRestrictions[code] = restricted;
   2341                     if (!restricted && isDefault(userRestrictions)) {
   2342                         perUserRestrictions.remove(userId);
   2343                         userRestrictions = null;
   2344                     }
   2345                     changed = true;
   2346                 }
   2347 
   2348                 if (userRestrictions != null) {
   2349                     final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackages);
   2350                     if (perUserExcludedPackages == null && !noExcludedPackages) {
   2351                         perUserExcludedPackages = new SparseArray<>();
   2352                     }
   2353                     if (perUserExcludedPackages != null && !Arrays.equals(excludedPackages,
   2354                             perUserExcludedPackages.get(userId))) {
   2355                         if (noExcludedPackages) {
   2356                             perUserExcludedPackages.remove(userId);
   2357                             if (perUserExcludedPackages.size() <= 0) {
   2358                                 perUserExcludedPackages = null;
   2359                             }
   2360                         } else {
   2361                             perUserExcludedPackages.put(userId, excludedPackages);
   2362                         }
   2363                         changed = true;
   2364                     }
   2365                 }
   2366             }
   2367 
   2368             return changed;
   2369         }
   2370 
   2371         public boolean hasRestriction(int restriction, String packageName, int userId) {
   2372             if (perUserRestrictions == null) {
   2373                 return false;
   2374             }
   2375             boolean[] restrictions = perUserRestrictions.get(userId);
   2376             if (restrictions == null) {
   2377                 return false;
   2378             }
   2379             if (!restrictions[restriction]) {
   2380                 return false;
   2381             }
   2382             if (perUserExcludedPackages == null) {
   2383                 return true;
   2384             }
   2385             String[] perUserExclusions = perUserExcludedPackages.get(userId);
   2386             if (perUserExclusions == null) {
   2387                 return true;
   2388             }
   2389             return !ArrayUtils.contains(perUserExclusions, packageName);
   2390         }
   2391 
   2392         public void removeUser(int userId) {
   2393             if (perUserExcludedPackages != null) {
   2394                 perUserExcludedPackages.remove(userId);
   2395                 if (perUserExcludedPackages.size() <= 0) {
   2396                     perUserExcludedPackages = null;
   2397                 }
   2398             }
   2399         }
   2400 
   2401         public boolean isDefault() {
   2402             return perUserRestrictions == null || perUserRestrictions.size() <= 0;
   2403         }
   2404 
   2405         @Override
   2406         public void binderDied() {
   2407             synchronized (AppOpsService.this) {
   2408                 mOpUserRestrictions.remove(token);
   2409                 if (perUserRestrictions == null) {
   2410                     return;
   2411                 }
   2412                 final int userCount = perUserRestrictions.size();
   2413                 for (int i = 0; i < userCount; i++) {
   2414                     final boolean[] restrictions = perUserRestrictions.valueAt(i);
   2415                     final int restrictionCount = restrictions.length;
   2416                     for (int j = 0; j < restrictionCount; j++) {
   2417                         if (restrictions[j]) {
   2418                             final int changedCode = j;
   2419                             mHandler.post(() -> notifyWatchersOfChange(changedCode));
   2420                         }
   2421                     }
   2422                 }
   2423                 destroy();
   2424             }
   2425         }
   2426 
   2427         public void destroy() {
   2428             token.unlinkToDeath(this, 0);
   2429         }
   2430 
   2431         private boolean isDefault(boolean[] array) {
   2432             if (ArrayUtils.isEmpty(array)) {
   2433                 return true;
   2434             }
   2435             for (boolean value : array) {
   2436                 if (value) {
   2437                     return false;
   2438                 }
   2439             }
   2440             return true;
   2441         }
   2442     }
   2443 }
   2444