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