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