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