1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import java.io.File; 20 import java.io.FileDescriptor; 21 import java.io.FileInputStream; 22 import java.io.FileNotFoundException; 23 import java.io.FileOutputStream; 24 import java.io.IOException; 25 import java.io.PrintWriter; 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.Iterator; 29 import java.util.List; 30 import java.util.Map; 31 32 import android.app.ActivityThread; 33 import android.app.AppOpsManager; 34 import android.content.Context; 35 import android.content.pm.ApplicationInfo; 36 import android.content.pm.IPackageManager; 37 import android.content.pm.PackageManager; 38 import android.content.pm.PackageManager.NameNotFoundException; 39 import android.media.AudioAttributes; 40 import android.os.AsyncTask; 41 import android.os.Binder; 42 import android.os.Bundle; 43 import android.os.Handler; 44 import android.os.IBinder; 45 import android.os.Process; 46 import android.os.RemoteException; 47 import android.os.ServiceManager; 48 import android.os.UserHandle; 49 import android.util.ArrayMap; 50 import android.util.ArraySet; 51 import android.util.AtomicFile; 52 import android.util.Log; 53 import android.util.Pair; 54 import android.util.Slog; 55 import android.util.SparseArray; 56 import android.util.SparseIntArray; 57 import android.util.TimeUtils; 58 import android.util.Xml; 59 60 import com.android.internal.app.IAppOpsService; 61 import com.android.internal.app.IAppOpsCallback; 62 import com.android.internal.util.FastXmlSerializer; 63 import com.android.internal.util.XmlUtils; 64 65 import org.xmlpull.v1.XmlPullParser; 66 import org.xmlpull.v1.XmlPullParserException; 67 import org.xmlpull.v1.XmlSerializer; 68 69 public 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 (getOpsRawLocked(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 if (uid == 0) { 773 packageName = "root"; 774 } else if (uid == Process.SHELL_UID) { 775 packageName = "com.android.shell"; 776 } 777 return getOpsRawLocked(uid, packageName, edit); 778 } 779 780 private Ops getOpsRawLocked(int uid, String packageName, boolean edit) { 781 HashMap<String, Ops> pkgOps = mUidOps.get(uid); 782 if (pkgOps == null) { 783 if (!edit) { 784 return null; 785 } 786 pkgOps = new HashMap<String, Ops>(); 787 mUidOps.put(uid, pkgOps); 788 } 789 Ops ops = pkgOps.get(packageName); 790 if (ops == null) { 791 if (!edit) { 792 return null; 793 } 794 boolean isPrivileged = false; 795 // This is the first time we have seen this package name under this uid, 796 // so let's make sure it is valid. 797 if (uid != 0) { 798 final long ident = Binder.clearCallingIdentity(); 799 try { 800 int pkgUid = -1; 801 try { 802 ApplicationInfo appInfo = ActivityThread.getPackageManager() 803 .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid)); 804 if (appInfo != null) { 805 pkgUid = appInfo.uid; 806 isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0; 807 } else { 808 if ("media".equals(packageName)) { 809 pkgUid = Process.MEDIA_UID; 810 isPrivileged = false; 811 } 812 } 813 } catch (RemoteException e) { 814 Slog.w(TAG, "Could not contact PackageManager", e); 815 } 816 if (pkgUid != uid) { 817 // Oops! The package name is not valid for the uid they are calling 818 // under. Abort. 819 Slog.w(TAG, "Bad call: specified package " + packageName 820 + " under uid " + uid + " but it is really " + pkgUid); 821 return null; 822 } 823 } finally { 824 Binder.restoreCallingIdentity(ident); 825 } 826 } 827 ops = new Ops(packageName, uid, isPrivileged); 828 pkgOps.put(packageName, ops); 829 } 830 return ops; 831 } 832 833 private void scheduleWriteLocked() { 834 if (!mWriteScheduled) { 835 mWriteScheduled = true; 836 mHandler.postDelayed(mWriteRunner, WRITE_DELAY); 837 } 838 } 839 840 private void scheduleWriteNowLocked() { 841 if (!mWriteScheduled) { 842 mWriteScheduled = true; 843 } 844 mHandler.removeCallbacks(mWriteRunner); 845 mHandler.post(mWriteRunner); 846 } 847 848 private Op getOpLocked(int code, int uid, String packageName, boolean edit) { 849 Ops ops = getOpsLocked(uid, packageName, edit); 850 if (ops == null) { 851 return null; 852 } 853 return getOpLocked(ops, code, edit); 854 } 855 856 private Op getOpLocked(Ops ops, int code, boolean edit) { 857 Op op = ops.get(code); 858 if (op == null) { 859 if (!edit) { 860 return null; 861 } 862 op = new Op(ops.uid, ops.packageName, code); 863 ops.put(code, op); 864 } 865 if (edit) { 866 scheduleWriteLocked(); 867 } 868 return op; 869 } 870 871 private boolean isOpRestricted(int uid, int code, String packageName) { 872 int userHandle = UserHandle.getUserId(uid); 873 boolean[] opRestrictions = mOpRestrictions.get(userHandle); 874 if ((opRestrictions != null) && opRestrictions[code]) { 875 if (AppOpsManager.opAllowSystemBypassRestriction(code)) { 876 synchronized (this) { 877 Ops ops = getOpsLocked(uid, packageName, true); 878 if ((ops != null) && ops.isPrivileged) { 879 return false; 880 } 881 } 882 } 883 return true; 884 } 885 return false; 886 } 887 888 void readState() { 889 synchronized (mFile) { 890 synchronized (this) { 891 FileInputStream stream; 892 try { 893 stream = mFile.openRead(); 894 } catch (FileNotFoundException e) { 895 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty"); 896 return; 897 } 898 boolean success = false; 899 try { 900 XmlPullParser parser = Xml.newPullParser(); 901 parser.setInput(stream, null); 902 int type; 903 while ((type = parser.next()) != XmlPullParser.START_TAG 904 && type != XmlPullParser.END_DOCUMENT) { 905 ; 906 } 907 908 if (type != XmlPullParser.START_TAG) { 909 throw new IllegalStateException("no start tag found"); 910 } 911 912 int outerDepth = parser.getDepth(); 913 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 914 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 915 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 916 continue; 917 } 918 919 String tagName = parser.getName(); 920 if (tagName.equals("pkg")) { 921 readPackage(parser); 922 } else { 923 Slog.w(TAG, "Unknown element under <app-ops>: " 924 + parser.getName()); 925 XmlUtils.skipCurrentTag(parser); 926 } 927 } 928 success = true; 929 } catch (IllegalStateException e) { 930 Slog.w(TAG, "Failed parsing " + e); 931 } catch (NullPointerException e) { 932 Slog.w(TAG, "Failed parsing " + e); 933 } catch (NumberFormatException e) { 934 Slog.w(TAG, "Failed parsing " + e); 935 } catch (XmlPullParserException e) { 936 Slog.w(TAG, "Failed parsing " + e); 937 } catch (IOException e) { 938 Slog.w(TAG, "Failed parsing " + e); 939 } catch (IndexOutOfBoundsException e) { 940 Slog.w(TAG, "Failed parsing " + e); 941 } finally { 942 if (!success) { 943 mUidOps.clear(); 944 } 945 try { 946 stream.close(); 947 } catch (IOException e) { 948 } 949 } 950 } 951 } 952 } 953 954 void readPackage(XmlPullParser parser) throws NumberFormatException, 955 XmlPullParserException, IOException { 956 String pkgName = parser.getAttributeValue(null, "n"); 957 int outerDepth = parser.getDepth(); 958 int type; 959 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 960 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 961 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 962 continue; 963 } 964 965 String tagName = parser.getName(); 966 if (tagName.equals("uid")) { 967 readUid(parser, pkgName); 968 } else { 969 Slog.w(TAG, "Unknown element under <pkg>: " 970 + parser.getName()); 971 XmlUtils.skipCurrentTag(parser); 972 } 973 } 974 } 975 976 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, 977 XmlPullParserException, IOException { 978 int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); 979 String isPrivilegedString = parser.getAttributeValue(null, "p"); 980 boolean isPrivileged = false; 981 if (isPrivilegedString == null) { 982 try { 983 IPackageManager packageManager = ActivityThread.getPackageManager(); 984 if (packageManager != null) { 985 ApplicationInfo appInfo = ActivityThread.getPackageManager() 986 .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid)); 987 if (appInfo != null) { 988 isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0; 989 } 990 } else { 991 // Could not load data, don't add to cache so it will be loaded later. 992 return; 993 } 994 } catch (RemoteException e) { 995 Slog.w(TAG, "Could not contact PackageManager", e); 996 } 997 } else { 998 isPrivileged = Boolean.parseBoolean(isPrivilegedString); 999 } 1000 int outerDepth = parser.getDepth(); 1001 int type; 1002 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1003 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1004 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1005 continue; 1006 } 1007 1008 String tagName = parser.getName(); 1009 if (tagName.equals("op")) { 1010 Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n"))); 1011 String mode = parser.getAttributeValue(null, "m"); 1012 if (mode != null) { 1013 op.mode = Integer.parseInt(mode); 1014 } 1015 String time = parser.getAttributeValue(null, "t"); 1016 if (time != null) { 1017 op.time = Long.parseLong(time); 1018 } 1019 time = parser.getAttributeValue(null, "r"); 1020 if (time != null) { 1021 op.rejectTime = Long.parseLong(time); 1022 } 1023 String dur = parser.getAttributeValue(null, "d"); 1024 if (dur != null) { 1025 op.duration = Integer.parseInt(dur); 1026 } 1027 HashMap<String, Ops> pkgOps = mUidOps.get(uid); 1028 if (pkgOps == null) { 1029 pkgOps = new HashMap<String, Ops>(); 1030 mUidOps.put(uid, pkgOps); 1031 } 1032 Ops ops = pkgOps.get(pkgName); 1033 if (ops == null) { 1034 ops = new Ops(pkgName, uid, isPrivileged); 1035 pkgOps.put(pkgName, ops); 1036 } 1037 ops.put(op.op, op); 1038 } else { 1039 Slog.w(TAG, "Unknown element under <pkg>: " 1040 + parser.getName()); 1041 XmlUtils.skipCurrentTag(parser); 1042 } 1043 } 1044 } 1045 1046 void writeState() { 1047 synchronized (mFile) { 1048 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null); 1049 1050 FileOutputStream stream; 1051 try { 1052 stream = mFile.startWrite(); 1053 } catch (IOException e) { 1054 Slog.w(TAG, "Failed to write state: " + e); 1055 return; 1056 } 1057 1058 try { 1059 XmlSerializer out = new FastXmlSerializer(); 1060 out.setOutput(stream, "utf-8"); 1061 out.startDocument(null, true); 1062 out.startTag(null, "app-ops"); 1063 1064 if (allOps != null) { 1065 String lastPkg = null; 1066 for (int i=0; i<allOps.size(); i++) { 1067 AppOpsManager.PackageOps pkg = allOps.get(i); 1068 if (!pkg.getPackageName().equals(lastPkg)) { 1069 if (lastPkg != null) { 1070 out.endTag(null, "pkg"); 1071 } 1072 lastPkg = pkg.getPackageName(); 1073 out.startTag(null, "pkg"); 1074 out.attribute(null, "n", lastPkg); 1075 } 1076 out.startTag(null, "uid"); 1077 out.attribute(null, "n", Integer.toString(pkg.getUid())); 1078 synchronized (this) { 1079 Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false); 1080 // Should always be present as the list of PackageOps is generated 1081 // from Ops. 1082 if (ops != null) { 1083 out.attribute(null, "p", Boolean.toString(ops.isPrivileged)); 1084 } else { 1085 out.attribute(null, "p", Boolean.toString(false)); 1086 } 1087 } 1088 List<AppOpsManager.OpEntry> ops = pkg.getOps(); 1089 for (int j=0; j<ops.size(); j++) { 1090 AppOpsManager.OpEntry op = ops.get(j); 1091 out.startTag(null, "op"); 1092 out.attribute(null, "n", Integer.toString(op.getOp())); 1093 if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) { 1094 out.attribute(null, "m", Integer.toString(op.getMode())); 1095 } 1096 long time = op.getTime(); 1097 if (time != 0) { 1098 out.attribute(null, "t", Long.toString(time)); 1099 } 1100 time = op.getRejectTime(); 1101 if (time != 0) { 1102 out.attribute(null, "r", Long.toString(time)); 1103 } 1104 int dur = op.getDuration(); 1105 if (dur != 0) { 1106 out.attribute(null, "d", Integer.toString(dur)); 1107 } 1108 out.endTag(null, "op"); 1109 } 1110 out.endTag(null, "uid"); 1111 } 1112 if (lastPkg != null) { 1113 out.endTag(null, "pkg"); 1114 } 1115 } 1116 1117 out.endTag(null, "app-ops"); 1118 out.endDocument(); 1119 mFile.finishWrite(stream); 1120 } catch (IOException e) { 1121 Slog.w(TAG, "Failed to write state, restoring backup.", e); 1122 mFile.failWrite(stream); 1123 } 1124 } 1125 } 1126 1127 @Override 1128 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1129 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1130 != PackageManager.PERMISSION_GRANTED) { 1131 pw.println("Permission Denial: can't dump ApOps service from from pid=" 1132 + Binder.getCallingPid() 1133 + ", uid=" + Binder.getCallingUid()); 1134 return; 1135 } 1136 1137 synchronized (this) { 1138 pw.println("Current AppOps Service state:"); 1139 final long now = System.currentTimeMillis(); 1140 boolean needSep = false; 1141 if (mOpModeWatchers.size() > 0) { 1142 needSep = true; 1143 pw.println(" Op mode watchers:"); 1144 for (int i=0; i<mOpModeWatchers.size(); i++) { 1145 pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); 1146 pw.println(":"); 1147 ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i); 1148 for (int j=0; j<callbacks.size(); j++) { 1149 pw.print(" #"); pw.print(j); pw.print(": "); 1150 pw.println(callbacks.get(j)); 1151 } 1152 } 1153 } 1154 if (mPackageModeWatchers.size() > 0) { 1155 needSep = true; 1156 pw.println(" Package mode watchers:"); 1157 for (int i=0; i<mPackageModeWatchers.size(); i++) { 1158 pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i)); 1159 pw.println(":"); 1160 ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i); 1161 for (int j=0; j<callbacks.size(); j++) { 1162 pw.print(" #"); pw.print(j); pw.print(": "); 1163 pw.println(callbacks.get(j)); 1164 } 1165 } 1166 } 1167 if (mModeWatchers.size() > 0) { 1168 needSep = true; 1169 pw.println(" All mode watchers:"); 1170 for (int i=0; i<mModeWatchers.size(); i++) { 1171 pw.print(" "); pw.print(mModeWatchers.keyAt(i)); 1172 pw.print(" -> "); pw.println(mModeWatchers.valueAt(i)); 1173 } 1174 } 1175 if (mClients.size() > 0) { 1176 needSep = true; 1177 pw.println(" Clients:"); 1178 for (int i=0; i<mClients.size(); i++) { 1179 pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":"); 1180 ClientState cs = mClients.valueAt(i); 1181 pw.print(" "); pw.println(cs); 1182 if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) { 1183 pw.println(" Started ops:"); 1184 for (int j=0; j<cs.mStartedOps.size(); j++) { 1185 Op op = cs.mStartedOps.get(j); 1186 pw.print(" "); pw.print("uid="); pw.print(op.uid); 1187 pw.print(" pkg="); pw.print(op.packageName); 1188 pw.print(" op="); pw.println(AppOpsManager.opToName(op.op)); 1189 } 1190 } 1191 } 1192 } 1193 if (mAudioRestrictions.size() > 0) { 1194 boolean printedHeader = false; 1195 for (int o=0; o<mAudioRestrictions.size(); o++) { 1196 final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o)); 1197 final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o); 1198 for (int i=0; i<restrictions.size(); i++) { 1199 if (!printedHeader){ 1200 pw.println(" Audio Restrictions:"); 1201 printedHeader = true; 1202 needSep = true; 1203 } 1204 final int usage = restrictions.keyAt(i); 1205 pw.print(" "); pw.print(op); 1206 pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage)); 1207 Restriction r = restrictions.valueAt(i); 1208 pw.print(": mode="); pw.println(r.mode); 1209 if (!r.exceptionPackages.isEmpty()) { 1210 pw.println(" Exceptions:"); 1211 for (int j=0; j<r.exceptionPackages.size(); j++) { 1212 pw.print(" "); pw.println(r.exceptionPackages.valueAt(j)); 1213 } 1214 } 1215 } 1216 } 1217 } 1218 if (needSep) { 1219 pw.println(); 1220 } 1221 for (int i=0; i<mUidOps.size(); i++) { 1222 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":"); 1223 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i); 1224 for (Ops ops : pkgOps.values()) { 1225 pw.print(" Package "); pw.print(ops.packageName); pw.println(":"); 1226 for (int j=0; j<ops.size(); j++) { 1227 Op op = ops.valueAt(j); 1228 pw.print(" "); pw.print(AppOpsManager.opToName(op.op)); 1229 pw.print(": mode="); pw.print(op.mode); 1230 if (op.time != 0) { 1231 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw); 1232 pw.print(" ago"); 1233 } 1234 if (op.rejectTime != 0) { 1235 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw); 1236 pw.print(" ago"); 1237 } 1238 if (op.duration == -1) { 1239 pw.println(" (running)"); 1240 } else { 1241 pw.print("; duration="); 1242 TimeUtils.formatDuration(op.duration, pw); 1243 pw.println(); 1244 } 1245 } 1246 } 1247 } 1248 } 1249 } 1250 1251 private static final class Restriction { 1252 private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>(); 1253 int mode; 1254 ArraySet<String> exceptionPackages = NO_EXCEPTIONS; 1255 } 1256 1257 @Override 1258 public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException { 1259 checkSystemUid("setUserRestrictions"); 1260 boolean[] opRestrictions = mOpRestrictions.get(userHandle); 1261 if (opRestrictions == null) { 1262 opRestrictions = new boolean[AppOpsManager._NUM_OP]; 1263 mOpRestrictions.put(userHandle, opRestrictions); 1264 } 1265 for (int i = 0; i < opRestrictions.length; ++i) { 1266 String restriction = AppOpsManager.opToRestriction(i); 1267 if (restriction != null) { 1268 opRestrictions[i] = restrictions.getBoolean(restriction, false); 1269 } else { 1270 opRestrictions[i] = false; 1271 } 1272 } 1273 } 1274 1275 @Override 1276 public void removeUser(int userHandle) throws RemoteException { 1277 checkSystemUid("removeUser"); 1278 mOpRestrictions.remove(userHandle); 1279 } 1280 1281 private void checkSystemUid(String function) { 1282 int uid = Binder.getCallingUid(); 1283 if (uid != Process.SYSTEM_UID) { 1284 throw new SecurityException(function + " must by called by the system"); 1285 } 1286 } 1287 1288 } 1289