AppOpsService.java revision 401de1785eccc946ed6c35e9b7fccab92b2022af
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 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 441 Binder.getCallingPid(), Binder.getCallingUid(), null); 442 HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null; 443 synchronized (this) { 444 boolean changed = false; 445 for (int i=mUidOps.size()-1; i>=0; i--) { 446 HashMap<String, Ops> packages = mUidOps.valueAt(i); 447 Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator(); 448 while (it.hasNext()) { 449 Map.Entry<String, Ops> ent = it.next(); 450 String packageName = ent.getKey(); 451 Ops pkgOps = ent.getValue(); 452 for (int j=pkgOps.size()-1; j>=0; j--) { 453 Op curOp = pkgOps.valueAt(j); 454 if (AppOpsManager.opAllowsReset(curOp.op) 455 && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) { 456 curOp.mode = AppOpsManager.opToDefaultMode(curOp.op); 457 changed = true; 458 callbacks = addCallbacks(callbacks, packageName, curOp.op, 459 mOpModeWatchers.get(curOp.op)); 460 callbacks = addCallbacks(callbacks, packageName, curOp.op, 461 mPackageModeWatchers.get(packageName)); 462 if (curOp.time == 0 && curOp.rejectTime == 0) { 463 pkgOps.removeAt(j); 464 } 465 } 466 } 467 if (pkgOps.size() == 0) { 468 it.remove(); 469 } 470 } 471 if (packages.size() == 0) { 472 mUidOps.removeAt(i); 473 } 474 } 475 if (changed) { 476 scheduleWriteNowLocked(); 477 } 478 } 479 if (callbacks != null) { 480 for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) { 481 Callback cb = ent.getKey(); 482 ArrayList<Pair<String, Integer>> reports = ent.getValue(); 483 for (int i=0; i<reports.size(); i++) { 484 Pair<String, Integer> rep = reports.get(i); 485 try { 486 cb.mCallback.opChanged(rep.second, rep.first); 487 } catch (RemoteException e) { 488 } 489 } 490 } 491 } 492 } 493 494 @Override 495 public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) { 496 synchronized (this) { 497 op = AppOpsManager.opToSwitch(op); 498 Callback cb = mModeWatchers.get(callback.asBinder()); 499 if (cb == null) { 500 cb = new Callback(callback); 501 mModeWatchers.put(callback.asBinder(), cb); 502 } 503 if (op != AppOpsManager.OP_NONE) { 504 ArrayList<Callback> cbs = mOpModeWatchers.get(op); 505 if (cbs == null) { 506 cbs = new ArrayList<Callback>(); 507 mOpModeWatchers.put(op, cbs); 508 } 509 cbs.add(cb); 510 } 511 if (packageName != null) { 512 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName); 513 if (cbs == null) { 514 cbs = new ArrayList<Callback>(); 515 mPackageModeWatchers.put(packageName, cbs); 516 } 517 cbs.add(cb); 518 } 519 } 520 } 521 522 @Override 523 public void stopWatchingMode(IAppOpsCallback callback) { 524 synchronized (this) { 525 Callback cb = mModeWatchers.remove(callback.asBinder()); 526 if (cb != null) { 527 cb.unlinkToDeath(); 528 for (int i=mOpModeWatchers.size()-1; i>=0; i--) { 529 ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i); 530 cbs.remove(cb); 531 if (cbs.size() <= 0) { 532 mOpModeWatchers.removeAt(i); 533 } 534 } 535 for (int i=mPackageModeWatchers.size()-1; i>=0; i--) { 536 ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i); 537 cbs.remove(cb); 538 if (cbs.size() <= 0) { 539 mPackageModeWatchers.removeAt(i); 540 } 541 } 542 } 543 } 544 } 545 546 @Override 547 public IBinder getToken(IBinder clientToken) { 548 synchronized (this) { 549 ClientState cs = mClients.get(clientToken); 550 if (cs == null) { 551 cs = new ClientState(clientToken); 552 mClients.put(clientToken, cs); 553 } 554 return cs; 555 } 556 } 557 558 @Override 559 public int checkOperation(int code, int uid, String packageName) { 560 verifyIncomingUid(uid); 561 verifyIncomingOp(code); 562 synchronized (this) { 563 if (isOpRestricted(uid, code, packageName)) { 564 return AppOpsManager.MODE_IGNORED; 565 } 566 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false); 567 if (op == null) { 568 return AppOpsManager.opToDefaultMode(code); 569 } 570 return op.mode; 571 } 572 } 573 574 @Override 575 public int checkAudioOperation(int code, int usage, int uid, String packageName) { 576 synchronized (this) { 577 final int mode = checkRestrictionLocked(code, usage, uid, packageName); 578 if (mode != AppOpsManager.MODE_ALLOWED) { 579 return mode; 580 } 581 } 582 return checkOperation(code, uid, packageName); 583 } 584 585 private int checkRestrictionLocked(int code, int usage, int uid, String packageName) { 586 final SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code); 587 if (usageRestrictions != null) { 588 final Restriction r = usageRestrictions.get(usage); 589 if (r != null && !r.exceptionPackages.contains(packageName)) { 590 return r.mode; 591 } 592 } 593 return AppOpsManager.MODE_ALLOWED; 594 } 595 596 @Override 597 public void setAudioRestriction(int code, int usage, int uid, int mode, 598 String[] exceptionPackages) { 599 verifyIncomingUid(uid); 600 verifyIncomingOp(code); 601 synchronized (this) { 602 SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code); 603 if (usageRestrictions == null) { 604 usageRestrictions = new SparseArray<Restriction>(); 605 mAudioRestrictions.put(code, usageRestrictions); 606 } 607 usageRestrictions.remove(usage); 608 if (mode != AppOpsManager.MODE_ALLOWED) { 609 final Restriction r = new Restriction(); 610 r.mode = mode; 611 if (exceptionPackages != null) { 612 final int N = exceptionPackages.length; 613 r.exceptionPackages = new ArraySet<String>(N); 614 for (int i = 0; i < N; i++) { 615 final String pkg = exceptionPackages[i]; 616 if (pkg != null) { 617 r.exceptionPackages.add(pkg.trim()); 618 } 619 } 620 } 621 usageRestrictions.put(usage, r); 622 } 623 } 624 } 625 626 @Override 627 public int checkPackage(int uid, String packageName) { 628 synchronized (this) { 629 if (getOpsLocked(uid, packageName, true) != null) { 630 return AppOpsManager.MODE_ALLOWED; 631 } else { 632 return AppOpsManager.MODE_ERRORED; 633 } 634 } 635 } 636 637 @Override 638 public int noteOperation(int code, int uid, String packageName) { 639 verifyIncomingUid(uid); 640 verifyIncomingOp(code); 641 synchronized (this) { 642 Ops ops = getOpsLocked(uid, packageName, true); 643 if (ops == null) { 644 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid 645 + " package " + packageName); 646 return AppOpsManager.MODE_ERRORED; 647 } 648 Op op = getOpLocked(ops, code, true); 649 if (isOpRestricted(uid, code, packageName)) { 650 return AppOpsManager.MODE_IGNORED; 651 } 652 if (op.duration == -1) { 653 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName 654 + " code " + code + " time=" + op.time + " duration=" + op.duration); 655 } 656 op.duration = 0; 657 final int switchCode = AppOpsManager.opToSwitch(code); 658 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; 659 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { 660 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " 661 + switchCode + " (" + code + ") uid " + uid + " package " + packageName); 662 op.rejectTime = System.currentTimeMillis(); 663 return switchOp.mode; 664 } 665 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid 666 + " package " + packageName); 667 op.time = System.currentTimeMillis(); 668 op.rejectTime = 0; 669 return AppOpsManager.MODE_ALLOWED; 670 } 671 } 672 673 @Override 674 public int startOperation(IBinder token, int code, int uid, String packageName) { 675 verifyIncomingUid(uid); 676 verifyIncomingOp(code); 677 ClientState client = (ClientState)token; 678 synchronized (this) { 679 Ops ops = getOpsLocked(uid, packageName, true); 680 if (ops == null) { 681 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid 682 + " package " + packageName); 683 return AppOpsManager.MODE_ERRORED; 684 } 685 Op op = getOpLocked(ops, code, true); 686 if (isOpRestricted(uid, code, packageName)) { 687 return AppOpsManager.MODE_IGNORED; 688 } 689 final int switchCode = AppOpsManager.opToSwitch(code); 690 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; 691 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { 692 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code " 693 + switchCode + " (" + code + ") uid " + uid + " package " + packageName); 694 op.rejectTime = System.currentTimeMillis(); 695 return switchOp.mode; 696 } 697 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid 698 + " package " + packageName); 699 if (op.nesting == 0) { 700 op.time = System.currentTimeMillis(); 701 op.rejectTime = 0; 702 op.duration = -1; 703 } 704 op.nesting++; 705 if (client.mStartedOps != null) { 706 client.mStartedOps.add(op); 707 } 708 return AppOpsManager.MODE_ALLOWED; 709 } 710 } 711 712 @Override 713 public void finishOperation(IBinder token, int code, int uid, String packageName) { 714 verifyIncomingUid(uid); 715 verifyIncomingOp(code); 716 ClientState client = (ClientState)token; 717 synchronized (this) { 718 Op op = getOpLocked(code, uid, packageName, true); 719 if (op == null) { 720 return; 721 } 722 if (client.mStartedOps != null) { 723 if (!client.mStartedOps.remove(op)) { 724 throw new IllegalStateException("Operation not started: uid" + op.uid 725 + " pkg=" + op.packageName + " op=" + op.op); 726 } 727 } 728 finishOperationLocked(op); 729 } 730 } 731 732 void finishOperationLocked(Op op) { 733 if (op.nesting <= 1) { 734 if (op.nesting == 1) { 735 op.duration = (int)(System.currentTimeMillis() - op.time); 736 op.time += op.duration; 737 } else { 738 Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg " 739 + op.packageName + " code " + op.op + " time=" + op.time 740 + " duration=" + op.duration + " nesting=" + op.nesting); 741 } 742 op.nesting = 0; 743 } else { 744 op.nesting--; 745 } 746 } 747 748 private void verifyIncomingUid(int uid) { 749 if (uid == Binder.getCallingUid()) { 750 return; 751 } 752 if (Binder.getCallingPid() == Process.myPid()) { 753 return; 754 } 755 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 756 Binder.getCallingPid(), Binder.getCallingUid(), null); 757 } 758 759 private void verifyIncomingOp(int op) { 760 if (op >= 0 && op < AppOpsManager._NUM_OP) { 761 return; 762 } 763 throw new IllegalArgumentException("Bad operation #" + op); 764 } 765 766 private Ops getOpsLocked(int uid, String packageName, boolean edit) { 767 HashMap<String, Ops> pkgOps = mUidOps.get(uid); 768 if (pkgOps == null) { 769 if (!edit) { 770 return null; 771 } 772 pkgOps = new HashMap<String, Ops>(); 773 mUidOps.put(uid, pkgOps); 774 } 775 if (uid == 0) { 776 packageName = "root"; 777 } else if (uid == Process.SHELL_UID) { 778 packageName = "com.android.shell"; 779 } 780 Ops ops = pkgOps.get(packageName); 781 if (ops == null) { 782 if (!edit) { 783 return null; 784 } 785 boolean isPrivileged = false; 786 // This is the first time we have seen this package name under this uid, 787 // so let's make sure it is valid. 788 if (uid != 0) { 789 final long ident = Binder.clearCallingIdentity(); 790 try { 791 int pkgUid = -1; 792 try { 793 ApplicationInfo appInfo = ActivityThread.getPackageManager() 794 .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid)); 795 if (appInfo != null) { 796 pkgUid = appInfo.uid; 797 isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0; 798 } else { 799 if ("media".equals(packageName)) { 800 pkgUid = Process.MEDIA_UID; 801 isPrivileged = false; 802 } 803 } 804 } catch (RemoteException e) { 805 Slog.w(TAG, "Could not contact PackageManager", e); 806 } 807 if (pkgUid != uid) { 808 // Oops! The package name is not valid for the uid they are calling 809 // under. Abort. 810 Slog.w(TAG, "Bad call: specified package " + packageName 811 + " under uid " + uid + " but it is really " + pkgUid); 812 return null; 813 } 814 } finally { 815 Binder.restoreCallingIdentity(ident); 816 } 817 } 818 ops = new Ops(packageName, uid, isPrivileged); 819 pkgOps.put(packageName, ops); 820 } 821 return ops; 822 } 823 824 private void scheduleWriteLocked() { 825 if (!mWriteScheduled) { 826 mWriteScheduled = true; 827 mHandler.postDelayed(mWriteRunner, WRITE_DELAY); 828 } 829 } 830 831 private void scheduleWriteNowLocked() { 832 if (!mWriteScheduled) { 833 mWriteScheduled = true; 834 } 835 mHandler.removeCallbacks(mWriteRunner); 836 mHandler.post(mWriteRunner); 837 } 838 839 private Op getOpLocked(int code, int uid, String packageName, boolean edit) { 840 Ops ops = getOpsLocked(uid, packageName, edit); 841 if (ops == null) { 842 return null; 843 } 844 return getOpLocked(ops, code, edit); 845 } 846 847 private Op getOpLocked(Ops ops, int code, boolean edit) { 848 Op op = ops.get(code); 849 if (op == null) { 850 if (!edit) { 851 return null; 852 } 853 op = new Op(ops.uid, ops.packageName, code); 854 ops.put(code, op); 855 } 856 if (edit) { 857 scheduleWriteLocked(); 858 } 859 return op; 860 } 861 862 private boolean isOpRestricted(int uid, int code, String packageName) { 863 int userHandle = UserHandle.getUserId(uid); 864 boolean[] opRestrictions = mOpRestrictions.get(userHandle); 865 if ((opRestrictions != null) && opRestrictions[code]) { 866 if (AppOpsManager.opAllowSystemBypassRestriction(code)) { 867 synchronized (this) { 868 Ops ops = getOpsLocked(uid, packageName, true); 869 if ((ops != null) && ops.isPrivileged) { 870 return false; 871 } 872 } 873 } 874 return true; 875 } 876 return false; 877 } 878 879 void readState() { 880 synchronized (mFile) { 881 synchronized (this) { 882 FileInputStream stream; 883 try { 884 stream = mFile.openRead(); 885 } catch (FileNotFoundException e) { 886 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty"); 887 return; 888 } 889 boolean success = false; 890 try { 891 XmlPullParser parser = Xml.newPullParser(); 892 parser.setInput(stream, null); 893 int type; 894 while ((type = parser.next()) != XmlPullParser.START_TAG 895 && type != XmlPullParser.END_DOCUMENT) { 896 ; 897 } 898 899 if (type != XmlPullParser.START_TAG) { 900 throw new IllegalStateException("no start tag found"); 901 } 902 903 int outerDepth = parser.getDepth(); 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("pkg")) { 912 readPackage(parser); 913 } else { 914 Slog.w(TAG, "Unknown element under <app-ops>: " 915 + parser.getName()); 916 XmlUtils.skipCurrentTag(parser); 917 } 918 } 919 success = true; 920 } catch (IllegalStateException e) { 921 Slog.w(TAG, "Failed parsing " + e); 922 } catch (NullPointerException e) { 923 Slog.w(TAG, "Failed parsing " + e); 924 } catch (NumberFormatException e) { 925 Slog.w(TAG, "Failed parsing " + e); 926 } catch (XmlPullParserException e) { 927 Slog.w(TAG, "Failed parsing " + e); 928 } catch (IOException e) { 929 Slog.w(TAG, "Failed parsing " + e); 930 } catch (IndexOutOfBoundsException e) { 931 Slog.w(TAG, "Failed parsing " + e); 932 } finally { 933 if (!success) { 934 mUidOps.clear(); 935 } 936 try { 937 stream.close(); 938 } catch (IOException e) { 939 } 940 } 941 } 942 } 943 } 944 945 void readPackage(XmlPullParser parser) throws NumberFormatException, 946 XmlPullParserException, IOException { 947 String pkgName = parser.getAttributeValue(null, "n"); 948 int outerDepth = parser.getDepth(); 949 int type; 950 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 951 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 952 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 953 continue; 954 } 955 956 String tagName = parser.getName(); 957 if (tagName.equals("uid")) { 958 readUid(parser, pkgName); 959 } else { 960 Slog.w(TAG, "Unknown element under <pkg>: " 961 + parser.getName()); 962 XmlUtils.skipCurrentTag(parser); 963 } 964 } 965 } 966 967 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, 968 XmlPullParserException, IOException { 969 int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); 970 String isPrivilegedString = parser.getAttributeValue(null, "p"); 971 boolean isPrivileged = false; 972 if (isPrivilegedString == null) { 973 try { 974 IPackageManager packageManager = ActivityThread.getPackageManager(); 975 if (packageManager != null) { 976 ApplicationInfo appInfo = ActivityThread.getPackageManager() 977 .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid)); 978 if (appInfo != null) { 979 isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0; 980 } 981 } else { 982 // Could not load data, don't add to cache so it will be loaded later. 983 return; 984 } 985 } catch (RemoteException e) { 986 Slog.w(TAG, "Could not contact PackageManager", e); 987 } 988 } else { 989 isPrivileged = Boolean.parseBoolean(isPrivilegedString); 990 } 991 int outerDepth = parser.getDepth(); 992 int type; 993 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 994 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 995 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 996 continue; 997 } 998 999 String tagName = parser.getName(); 1000 if (tagName.equals("op")) { 1001 Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n"))); 1002 String mode = parser.getAttributeValue(null, "m"); 1003 if (mode != null) { 1004 op.mode = Integer.parseInt(mode); 1005 } 1006 String time = parser.getAttributeValue(null, "t"); 1007 if (time != null) { 1008 op.time = Long.parseLong(time); 1009 } 1010 time = parser.getAttributeValue(null, "r"); 1011 if (time != null) { 1012 op.rejectTime = Long.parseLong(time); 1013 } 1014 String dur = parser.getAttributeValue(null, "d"); 1015 if (dur != null) { 1016 op.duration = Integer.parseInt(dur); 1017 } 1018 HashMap<String, Ops> pkgOps = mUidOps.get(uid); 1019 if (pkgOps == null) { 1020 pkgOps = new HashMap<String, Ops>(); 1021 mUidOps.put(uid, pkgOps); 1022 } 1023 Ops ops = pkgOps.get(pkgName); 1024 if (ops == null) { 1025 ops = new Ops(pkgName, uid, isPrivileged); 1026 pkgOps.put(pkgName, ops); 1027 } 1028 ops.put(op.op, op); 1029 } else { 1030 Slog.w(TAG, "Unknown element under <pkg>: " 1031 + parser.getName()); 1032 XmlUtils.skipCurrentTag(parser); 1033 } 1034 } 1035 } 1036 1037 void writeState() { 1038 synchronized (mFile) { 1039 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null); 1040 1041 FileOutputStream stream; 1042 try { 1043 stream = mFile.startWrite(); 1044 } catch (IOException e) { 1045 Slog.w(TAG, "Failed to write state: " + e); 1046 return; 1047 } 1048 1049 try { 1050 XmlSerializer out = new FastXmlSerializer(); 1051 out.setOutput(stream, "utf-8"); 1052 out.startDocument(null, true); 1053 out.startTag(null, "app-ops"); 1054 1055 if (allOps != null) { 1056 String lastPkg = null; 1057 for (int i=0; i<allOps.size(); i++) { 1058 AppOpsManager.PackageOps pkg = allOps.get(i); 1059 if (!pkg.getPackageName().equals(lastPkg)) { 1060 if (lastPkg != null) { 1061 out.endTag(null, "pkg"); 1062 } 1063 lastPkg = pkg.getPackageName(); 1064 out.startTag(null, "pkg"); 1065 out.attribute(null, "n", lastPkg); 1066 } 1067 out.startTag(null, "uid"); 1068 out.attribute(null, "n", Integer.toString(pkg.getUid())); 1069 synchronized (this) { 1070 Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false); 1071 // Should always be present as the list of PackageOps is generated 1072 // from Ops. 1073 if (ops != null) { 1074 out.attribute(null, "p", Boolean.toString(ops.isPrivileged)); 1075 } else { 1076 out.attribute(null, "p", Boolean.toString(false)); 1077 } 1078 } 1079 List<AppOpsManager.OpEntry> ops = pkg.getOps(); 1080 for (int j=0; j<ops.size(); j++) { 1081 AppOpsManager.OpEntry op = ops.get(j); 1082 out.startTag(null, "op"); 1083 out.attribute(null, "n", Integer.toString(op.getOp())); 1084 if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) { 1085 out.attribute(null, "m", Integer.toString(op.getMode())); 1086 } 1087 long time = op.getTime(); 1088 if (time != 0) { 1089 out.attribute(null, "t", Long.toString(time)); 1090 } 1091 time = op.getRejectTime(); 1092 if (time != 0) { 1093 out.attribute(null, "r", Long.toString(time)); 1094 } 1095 int dur = op.getDuration(); 1096 if (dur != 0) { 1097 out.attribute(null, "d", Integer.toString(dur)); 1098 } 1099 out.endTag(null, "op"); 1100 } 1101 out.endTag(null, "uid"); 1102 } 1103 if (lastPkg != null) { 1104 out.endTag(null, "pkg"); 1105 } 1106 } 1107 1108 out.endTag(null, "app-ops"); 1109 out.endDocument(); 1110 mFile.finishWrite(stream); 1111 } catch (IOException e) { 1112 Slog.w(TAG, "Failed to write state, restoring backup.", e); 1113 mFile.failWrite(stream); 1114 } 1115 } 1116 } 1117 1118 @Override 1119 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1120 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1121 != PackageManager.PERMISSION_GRANTED) { 1122 pw.println("Permission Denial: can't dump ApOps service from from pid=" 1123 + Binder.getCallingPid() 1124 + ", uid=" + Binder.getCallingUid()); 1125 return; 1126 } 1127 1128 synchronized (this) { 1129 pw.println("Current AppOps Service state:"); 1130 final long now = System.currentTimeMillis(); 1131 boolean needSep = false; 1132 if (mOpModeWatchers.size() > 0) { 1133 needSep = true; 1134 pw.println(" Op mode watchers:"); 1135 for (int i=0; i<mOpModeWatchers.size(); i++) { 1136 pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); 1137 pw.println(":"); 1138 ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i); 1139 for (int j=0; j<callbacks.size(); j++) { 1140 pw.print(" #"); pw.print(j); pw.print(": "); 1141 pw.println(callbacks.get(j)); 1142 } 1143 } 1144 } 1145 if (mPackageModeWatchers.size() > 0) { 1146 needSep = true; 1147 pw.println(" Package mode watchers:"); 1148 for (int i=0; i<mPackageModeWatchers.size(); i++) { 1149 pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i)); 1150 pw.println(":"); 1151 ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i); 1152 for (int j=0; j<callbacks.size(); j++) { 1153 pw.print(" #"); pw.print(j); pw.print(": "); 1154 pw.println(callbacks.get(j)); 1155 } 1156 } 1157 } 1158 if (mModeWatchers.size() > 0) { 1159 needSep = true; 1160 pw.println(" All mode watchers:"); 1161 for (int i=0; i<mModeWatchers.size(); i++) { 1162 pw.print(" "); pw.print(mModeWatchers.keyAt(i)); 1163 pw.print(" -> "); pw.println(mModeWatchers.valueAt(i)); 1164 } 1165 } 1166 if (mClients.size() > 0) { 1167 needSep = true; 1168 pw.println(" Clients:"); 1169 for (int i=0; i<mClients.size(); i++) { 1170 pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":"); 1171 ClientState cs = mClients.valueAt(i); 1172 pw.print(" "); pw.println(cs); 1173 if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) { 1174 pw.println(" Started ops:"); 1175 for (int j=0; j<cs.mStartedOps.size(); j++) { 1176 Op op = cs.mStartedOps.get(j); 1177 pw.print(" "); pw.print("uid="); pw.print(op.uid); 1178 pw.print(" pkg="); pw.print(op.packageName); 1179 pw.print(" op="); pw.println(AppOpsManager.opToName(op.op)); 1180 } 1181 } 1182 } 1183 } 1184 if (mAudioRestrictions.size() > 0) { 1185 boolean printedHeader = false; 1186 for (int o=0; o<mAudioRestrictions.size(); o++) { 1187 final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o)); 1188 final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o); 1189 for (int i=0; i<restrictions.size(); i++) { 1190 if (!printedHeader){ 1191 pw.println(" Audio Restrictions:"); 1192 printedHeader = true; 1193 needSep = true; 1194 } 1195 final int usage = restrictions.keyAt(i); 1196 pw.print(" "); pw.print(op); 1197 pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage)); 1198 Restriction r = restrictions.valueAt(i); 1199 pw.print(": mode="); pw.println(r.mode); 1200 if (!r.exceptionPackages.isEmpty()) { 1201 pw.println(" Exceptions:"); 1202 for (int j=0; j<r.exceptionPackages.size(); j++) { 1203 pw.print(" "); pw.println(r.exceptionPackages.valueAt(j)); 1204 } 1205 } 1206 } 1207 } 1208 } 1209 if (needSep) { 1210 pw.println(); 1211 } 1212 for (int i=0; i<mUidOps.size(); i++) { 1213 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":"); 1214 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i); 1215 for (Ops ops : pkgOps.values()) { 1216 pw.print(" Package "); pw.print(ops.packageName); pw.println(":"); 1217 for (int j=0; j<ops.size(); j++) { 1218 Op op = ops.valueAt(j); 1219 pw.print(" "); pw.print(AppOpsManager.opToName(op.op)); 1220 pw.print(": mode="); pw.print(op.mode); 1221 if (op.time != 0) { 1222 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw); 1223 pw.print(" ago"); 1224 } 1225 if (op.rejectTime != 0) { 1226 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw); 1227 pw.print(" ago"); 1228 } 1229 if (op.duration == -1) { 1230 pw.println(" (running)"); 1231 } else { 1232 pw.print("; duration="); 1233 TimeUtils.formatDuration(op.duration, pw); 1234 pw.println(); 1235 } 1236 } 1237 } 1238 } 1239 } 1240 } 1241 1242 private static final class Restriction { 1243 private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>(); 1244 int mode; 1245 ArraySet<String> exceptionPackages = NO_EXCEPTIONS; 1246 } 1247 1248 @Override 1249 public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException { 1250 checkSystemUid("setUserRestrictions"); 1251 boolean[] opRestrictions = mOpRestrictions.get(userHandle); 1252 if (opRestrictions == null) { 1253 opRestrictions = new boolean[AppOpsManager._NUM_OP]; 1254 mOpRestrictions.put(userHandle, opRestrictions); 1255 } 1256 for (int i = 0; i < opRestrictions.length; ++i) { 1257 String restriction = AppOpsManager.opToRestriction(i); 1258 if (restriction != null) { 1259 opRestrictions[i] = restrictions.getBoolean(restriction, false); 1260 } else { 1261 opRestrictions[i] = false; 1262 } 1263 } 1264 } 1265 1266 @Override 1267 public void removeUser(int userHandle) throws RemoteException { 1268 checkSystemUid("removeUser"); 1269 mOpRestrictions.remove(userHandle); 1270 } 1271 1272 private void checkSystemUid(String function) { 1273 int uid = Binder.getCallingUid(); 1274 if (uid != Process.SYSTEM_UID) { 1275 throw new SecurityException(function + " must by called by the system"); 1276 } 1277 } 1278 1279} 1280