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