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 Ops(String _packageName, int _uid)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 Op(int _op)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 Callback(IAppOpsCallback callback)125 public Callback(IAppOpsCallback callback) { 126 mCallback = callback; 127 try { 128 mCallback.asBinder().linkToDeath(this, 0); 129 } catch (RemoteException e) { 130 } 131 } 132 unlinkToDeath()133 public void unlinkToDeath() { 134 mCallback.asBinder().unlinkToDeath(this, 0); 135 } 136 137 @Override binderDied()138 public void binderDied() { 139 stopWatchingMode(mCallback); 140 } 141 } 142 AppOpsService(File storagePath)143 public AppOpsService(File storagePath) { 144 mFile = new AtomicFile(storagePath); 145 mHandler = new Handler(); 146 readState(); 147 } 148 publish(Context context)149 public void publish(Context context) { 150 mContext = context; 151 ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder()); 152 } 153 systemReady()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 packageRemoved(int uid, String packageName)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 uidRemoved(int uid)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 shutdown()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 collectOps(Ops pkgOps, int[] ops)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 getPackagesForOps(int[] ops)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 getOpsForPackage(int uid, String packageName, int[] ops)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 pruneOp(Op op, int uid, String packageName)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 setMode(int code, int uid, String packageName, int mode)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 addCallbacks( HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks, String packageName, int op, ArrayList<Callback> cbs)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 resetAllModes()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=mUidOps.size()-1; i>=0; i--) { 384 HashMap<String, Ops> packages = mUidOps.valueAt(i); 385 Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator(); 386 while (it.hasNext()) { 387 Map.Entry<String, Ops> ent = it.next(); 388 String packageName = ent.getKey(); 389 Ops pkgOps = ent.getValue(); 390 for (int j=pkgOps.size()-1; j>=0; j--) { 391 Op curOp = pkgOps.valueAt(j); 392 if (curOp.mode != AppOpsManager.MODE_ALLOWED) { 393 curOp.mode = AppOpsManager.MODE_ALLOWED; 394 changed = true; 395 callbacks = addCallbacks(callbacks, packageName, curOp.op, 396 mOpModeWatchers.get(curOp.op)); 397 callbacks = addCallbacks(callbacks, packageName, curOp.op, 398 mPackageModeWatchers.get(packageName)); 399 if (curOp.time == 0 && curOp.rejectTime == 0) { 400 pkgOps.removeAt(j); 401 } 402 } 403 } 404 if (pkgOps.size() == 0) { 405 it.remove(); 406 } 407 } 408 if (packages.size() == 0) { 409 mUidOps.removeAt(i); 410 } 411 } 412 if (changed) { 413 scheduleWriteNowLocked(); 414 } 415 } 416 if (callbacks != null) { 417 for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) { 418 Callback cb = ent.getKey(); 419 ArrayList<Pair<String, Integer>> reports = ent.getValue(); 420 for (int i=0; i<reports.size(); i++) { 421 Pair<String, Integer> rep = reports.get(i); 422 try { 423 cb.mCallback.opChanged(rep.second, rep.first); 424 } catch (RemoteException e) { 425 } 426 } 427 } 428 } 429 } 430 431 @Override startWatchingMode(int op, String packageName, IAppOpsCallback callback)432 public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) { 433 synchronized (this) { 434 op = AppOpsManager.opToSwitch(op); 435 Callback cb = mModeWatchers.get(callback.asBinder()); 436 if (cb == null) { 437 cb = new Callback(callback); 438 mModeWatchers.put(callback.asBinder(), cb); 439 } 440 if (op != AppOpsManager.OP_NONE) { 441 ArrayList<Callback> cbs = mOpModeWatchers.get(op); 442 if (cbs == null) { 443 cbs = new ArrayList<Callback>(); 444 mOpModeWatchers.put(op, cbs); 445 } 446 cbs.add(cb); 447 } 448 if (packageName != null) { 449 ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName); 450 if (cbs == null) { 451 cbs = new ArrayList<Callback>(); 452 mPackageModeWatchers.put(packageName, cbs); 453 } 454 cbs.add(cb); 455 } 456 } 457 } 458 459 @Override stopWatchingMode(IAppOpsCallback callback)460 public void stopWatchingMode(IAppOpsCallback callback) { 461 synchronized (this) { 462 Callback cb = mModeWatchers.remove(callback.asBinder()); 463 if (cb != null) { 464 cb.unlinkToDeath(); 465 for (int i=0; i<mOpModeWatchers.size(); i++) { 466 ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i); 467 cbs.remove(cb); 468 if (cbs.size() <= 0) { 469 mOpModeWatchers.removeAt(i); 470 } 471 } 472 if (mPackageModeWatchers.size() > 0) { 473 Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator(); 474 while (it.hasNext()) { 475 ArrayList<Callback> cbs = it.next(); 476 cbs.remove(cb); 477 if (cbs.size() <= 0) { 478 it.remove(); 479 } 480 } 481 } 482 } 483 } 484 } 485 486 @Override checkOperation(int code, int uid, String packageName)487 public int checkOperation(int code, int uid, String packageName) { 488 verifyIncomingUid(uid); 489 verifyIncomingOp(code); 490 synchronized (this) { 491 Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false); 492 if (op == null) { 493 return AppOpsManager.MODE_ALLOWED; 494 } 495 return op.mode; 496 } 497 } 498 499 @Override noteOperation(int code, int uid, String packageName)500 public int noteOperation(int code, int uid, String packageName) { 501 verifyIncomingUid(uid); 502 verifyIncomingOp(code); 503 synchronized (this) { 504 Ops ops = getOpsLocked(uid, packageName, true); 505 if (ops == null) { 506 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid 507 + " package " + packageName); 508 return AppOpsManager.MODE_IGNORED; 509 } 510 Op op = getOpLocked(ops, code, true); 511 if (op.duration == -1) { 512 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName 513 + " code " + code + " time=" + op.time + " duration=" + op.duration); 514 } 515 op.duration = 0; 516 final int switchCode = AppOpsManager.opToSwitch(code); 517 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; 518 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { 519 if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " 520 + switchCode + " (" + code + ") uid " + uid + " package " + packageName); 521 op.rejectTime = System.currentTimeMillis(); 522 return switchOp.mode; 523 } 524 if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid 525 + " package " + packageName); 526 op.time = System.currentTimeMillis(); 527 op.rejectTime = 0; 528 return AppOpsManager.MODE_ALLOWED; 529 } 530 } 531 532 @Override startOperation(int code, int uid, String packageName)533 public int startOperation(int code, int uid, String packageName) { 534 verifyIncomingUid(uid); 535 verifyIncomingOp(code); 536 synchronized (this) { 537 Ops ops = getOpsLocked(uid, packageName, true); 538 if (ops == null) { 539 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid 540 + " package " + packageName); 541 return AppOpsManager.MODE_IGNORED; 542 } 543 Op op = getOpLocked(ops, code, true); 544 final int switchCode = AppOpsManager.opToSwitch(code); 545 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; 546 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { 547 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code " 548 + switchCode + " (" + code + ") uid " + uid + " package " + packageName); 549 op.rejectTime = System.currentTimeMillis(); 550 return switchOp.mode; 551 } 552 if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid 553 + " package " + packageName); 554 if (op.nesting == 0) { 555 op.time = System.currentTimeMillis(); 556 op.rejectTime = 0; 557 op.duration = -1; 558 } 559 op.nesting++; 560 return AppOpsManager.MODE_ALLOWED; 561 } 562 } 563 564 @Override finishOperation(int code, int uid, String packageName)565 public void finishOperation(int code, int uid, String packageName) { 566 verifyIncomingUid(uid); 567 verifyIncomingOp(code); 568 synchronized (this) { 569 Op op = getOpLocked(code, uid, packageName, true); 570 if (op == null) { 571 return; 572 } 573 if (op.nesting <= 1) { 574 if (op.nesting == 1) { 575 op.duration = (int)(System.currentTimeMillis() - op.time); 576 op.time += op.duration; 577 } else { 578 Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName 579 + " code " + code + " time=" + op.time + " duration=" + op.duration 580 + " nesting=" + op.nesting); 581 } 582 op.nesting = 0; 583 } else { 584 op.nesting--; 585 } 586 } 587 } 588 verifyIncomingUid(int uid)589 private void verifyIncomingUid(int uid) { 590 if (uid == Binder.getCallingUid()) { 591 return; 592 } 593 if (Binder.getCallingPid() == Process.myPid()) { 594 return; 595 } 596 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 597 Binder.getCallingPid(), Binder.getCallingUid(), null); 598 } 599 verifyIncomingOp(int op)600 private void verifyIncomingOp(int op) { 601 if (op >= 0 && op < AppOpsManager._NUM_OP) { 602 return; 603 } 604 throw new IllegalArgumentException("Bad operation #" + op); 605 } 606 getOpsLocked(int uid, String packageName, boolean edit)607 private Ops getOpsLocked(int uid, String packageName, boolean edit) { 608 HashMap<String, Ops> pkgOps = mUidOps.get(uid); 609 if (pkgOps == null) { 610 if (!edit) { 611 return null; 612 } 613 pkgOps = new HashMap<String, Ops>(); 614 mUidOps.put(uid, pkgOps); 615 } 616 if (uid == 0) { 617 packageName = "root"; 618 } else if (uid == Process.SHELL_UID) { 619 packageName = "com.android.shell"; 620 } 621 Ops ops = pkgOps.get(packageName); 622 if (ops == null) { 623 if (!edit) { 624 return null; 625 } 626 // This is the first time we have seen this package name under this uid, 627 // so let's make sure it is valid. 628 if (uid != 0) { 629 final long ident = Binder.clearCallingIdentity(); 630 try { 631 int pkgUid = -1; 632 try { 633 pkgUid = mContext.getPackageManager().getPackageUid(packageName, 634 UserHandle.getUserId(uid)); 635 } catch (NameNotFoundException e) { 636 } 637 if (pkgUid != uid) { 638 // Oops! The package name is not valid for the uid they are calling 639 // under. Abort. 640 Slog.w(TAG, "Bad call: specified package " + packageName 641 + " under uid " + uid + " but it is really " + pkgUid); 642 return null; 643 } 644 } finally { 645 Binder.restoreCallingIdentity(ident); 646 } 647 } 648 ops = new Ops(packageName, uid); 649 pkgOps.put(packageName, ops); 650 } 651 return ops; 652 } 653 scheduleWriteLocked()654 private void scheduleWriteLocked() { 655 if (!mWriteScheduled) { 656 mWriteScheduled = true; 657 mHandler.postDelayed(mWriteRunner, WRITE_DELAY); 658 } 659 } 660 scheduleWriteNowLocked()661 private void scheduleWriteNowLocked() { 662 if (!mWriteScheduled) { 663 mWriteScheduled = true; 664 } 665 mHandler.removeCallbacks(mWriteRunner); 666 mHandler.post(mWriteRunner); 667 } 668 getOpLocked(int code, int uid, String packageName, boolean edit)669 private Op getOpLocked(int code, int uid, String packageName, boolean edit) { 670 Ops ops = getOpsLocked(uid, packageName, edit); 671 if (ops == null) { 672 return null; 673 } 674 return getOpLocked(ops, code, edit); 675 } 676 getOpLocked(Ops ops, int code, boolean edit)677 private Op getOpLocked(Ops ops, int code, boolean edit) { 678 Op op = ops.get(code); 679 if (op == null) { 680 if (!edit) { 681 return null; 682 } 683 op = new Op(code); 684 ops.put(code, op); 685 } 686 if (edit) { 687 scheduleWriteLocked(); 688 } 689 return op; 690 } 691 readState()692 void readState() { 693 synchronized (mFile) { 694 synchronized (this) { 695 FileInputStream stream; 696 try { 697 stream = mFile.openRead(); 698 } catch (FileNotFoundException e) { 699 Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty"); 700 return; 701 } 702 boolean success = false; 703 try { 704 XmlPullParser parser = Xml.newPullParser(); 705 parser.setInput(stream, null); 706 int type; 707 while ((type = parser.next()) != XmlPullParser.START_TAG 708 && type != XmlPullParser.END_DOCUMENT) { 709 ; 710 } 711 712 if (type != XmlPullParser.START_TAG) { 713 throw new IllegalStateException("no start tag found"); 714 } 715 716 int outerDepth = parser.getDepth(); 717 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 718 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 719 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 720 continue; 721 } 722 723 String tagName = parser.getName(); 724 if (tagName.equals("pkg")) { 725 readPackage(parser); 726 } else { 727 Slog.w(TAG, "Unknown element under <app-ops>: " 728 + parser.getName()); 729 XmlUtils.skipCurrentTag(parser); 730 } 731 } 732 success = true; 733 } catch (IllegalStateException e) { 734 Slog.w(TAG, "Failed parsing " + e); 735 } catch (NullPointerException e) { 736 Slog.w(TAG, "Failed parsing " + e); 737 } catch (NumberFormatException e) { 738 Slog.w(TAG, "Failed parsing " + e); 739 } catch (XmlPullParserException e) { 740 Slog.w(TAG, "Failed parsing " + e); 741 } catch (IOException e) { 742 Slog.w(TAG, "Failed parsing " + e); 743 } catch (IndexOutOfBoundsException e) { 744 Slog.w(TAG, "Failed parsing " + e); 745 } finally { 746 if (!success) { 747 mUidOps.clear(); 748 } 749 try { 750 stream.close(); 751 } catch (IOException e) { 752 } 753 } 754 } 755 } 756 } 757 readPackage(XmlPullParser parser)758 void readPackage(XmlPullParser parser) throws NumberFormatException, 759 XmlPullParserException, IOException { 760 String pkgName = parser.getAttributeValue(null, "n"); 761 int outerDepth = parser.getDepth(); 762 int type; 763 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 764 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 765 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 766 continue; 767 } 768 769 String tagName = parser.getName(); 770 if (tagName.equals("uid")) { 771 readUid(parser, pkgName); 772 } else { 773 Slog.w(TAG, "Unknown element under <pkg>: " 774 + parser.getName()); 775 XmlUtils.skipCurrentTag(parser); 776 } 777 } 778 } 779 readUid(XmlPullParser parser, String pkgName)780 void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, 781 XmlPullParserException, IOException { 782 int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); 783 int outerDepth = parser.getDepth(); 784 int type; 785 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 786 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 787 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 788 continue; 789 } 790 791 String tagName = parser.getName(); 792 if (tagName.equals("op")) { 793 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n"))); 794 String mode = parser.getAttributeValue(null, "m"); 795 if (mode != null) { 796 op.mode = Integer.parseInt(mode); 797 } 798 String time = parser.getAttributeValue(null, "t"); 799 if (time != null) { 800 op.time = Long.parseLong(time); 801 } 802 time = parser.getAttributeValue(null, "r"); 803 if (time != null) { 804 op.rejectTime = Long.parseLong(time); 805 } 806 String dur = parser.getAttributeValue(null, "d"); 807 if (dur != null) { 808 op.duration = Integer.parseInt(dur); 809 } 810 HashMap<String, Ops> pkgOps = mUidOps.get(uid); 811 if (pkgOps == null) { 812 pkgOps = new HashMap<String, Ops>(); 813 mUidOps.put(uid, pkgOps); 814 } 815 Ops ops = pkgOps.get(pkgName); 816 if (ops == null) { 817 ops = new Ops(pkgName, uid); 818 pkgOps.put(pkgName, ops); 819 } 820 ops.put(op.op, op); 821 } else { 822 Slog.w(TAG, "Unknown element under <pkg>: " 823 + parser.getName()); 824 XmlUtils.skipCurrentTag(parser); 825 } 826 } 827 } 828 writeState()829 void writeState() { 830 synchronized (mFile) { 831 List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null); 832 833 FileOutputStream stream; 834 try { 835 stream = mFile.startWrite(); 836 } catch (IOException e) { 837 Slog.w(TAG, "Failed to write state: " + e); 838 return; 839 } 840 841 try { 842 XmlSerializer out = new FastXmlSerializer(); 843 out.setOutput(stream, "utf-8"); 844 out.startDocument(null, true); 845 out.startTag(null, "app-ops"); 846 847 if (allOps != null) { 848 String lastPkg = null; 849 for (int i=0; i<allOps.size(); i++) { 850 AppOpsManager.PackageOps pkg = allOps.get(i); 851 if (!pkg.getPackageName().equals(lastPkg)) { 852 if (lastPkg != null) { 853 out.endTag(null, "pkg"); 854 } 855 lastPkg = pkg.getPackageName(); 856 out.startTag(null, "pkg"); 857 out.attribute(null, "n", lastPkg); 858 } 859 out.startTag(null, "uid"); 860 out.attribute(null, "n", Integer.toString(pkg.getUid())); 861 List<AppOpsManager.OpEntry> ops = pkg.getOps(); 862 for (int j=0; j<ops.size(); j++) { 863 AppOpsManager.OpEntry op = ops.get(j); 864 out.startTag(null, "op"); 865 out.attribute(null, "n", Integer.toString(op.getOp())); 866 if (op.getMode() != AppOpsManager.MODE_ALLOWED) { 867 out.attribute(null, "m", Integer.toString(op.getMode())); 868 } 869 long time = op.getTime(); 870 if (time != 0) { 871 out.attribute(null, "t", Long.toString(time)); 872 } 873 time = op.getRejectTime(); 874 if (time != 0) { 875 out.attribute(null, "r", Long.toString(time)); 876 } 877 int dur = op.getDuration(); 878 if (dur != 0) { 879 out.attribute(null, "d", Integer.toString(dur)); 880 } 881 out.endTag(null, "op"); 882 } 883 out.endTag(null, "uid"); 884 } 885 if (lastPkg != null) { 886 out.endTag(null, "pkg"); 887 } 888 } 889 890 out.endTag(null, "app-ops"); 891 out.endDocument(); 892 mFile.finishWrite(stream); 893 } catch (IOException e) { 894 Slog.w(TAG, "Failed to write state, restoring backup.", e); 895 mFile.failWrite(stream); 896 } 897 } 898 } 899 900 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)901 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 902 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 903 != PackageManager.PERMISSION_GRANTED) { 904 pw.println("Permission Denial: can't dump ApOps service from from pid=" 905 + Binder.getCallingPid() 906 + ", uid=" + Binder.getCallingUid()); 907 return; 908 } 909 910 synchronized (this) { 911 pw.println("Current AppOps Service state:"); 912 final long now = System.currentTimeMillis(); 913 for (int i=0; i<mUidOps.size(); i++) { 914 pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":"); 915 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i); 916 for (Ops ops : pkgOps.values()) { 917 pw.print(" Package "); pw.print(ops.packageName); pw.println(":"); 918 for (int j=0; j<ops.size(); j++) { 919 Op op = ops.valueAt(j); 920 pw.print(" "); pw.print(AppOpsManager.opToName(op.op)); 921 pw.print(": mode="); pw.print(op.mode); 922 if (op.time != 0) { 923 pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw); 924 pw.print(" ago"); 925 } 926 if (op.rejectTime != 0) { 927 pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw); 928 pw.print(" ago"); 929 } 930 if (op.duration == -1) { 931 pw.println(" (running)"); 932 } else { 933 pw.print("; duration="); 934 TimeUtils.formatDuration(op.duration, pw); 935 pw.println(); 936 } 937 } 938 } 939 } 940 } 941 } 942 } 943