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