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