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