1 /* 2 * Copyright (C) 2022 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.am; 18 19 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 20 import static android.Manifest.permission.CAMERA; 21 import static android.Manifest.permission.RECORD_AUDIO; 22 import static android.app.AppOpsManager.OPSTR_CAMERA; 23 import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; 24 import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; 25 import static android.app.AppOpsManager.OP_NONE; 26 import static android.app.AppOpsManager.opToPublicName; 27 import static android.app.AppOpsManager.strOpToOp; 28 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 29 import static android.os.Process.SYSTEM_UID; 30 31 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 32 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 33 import static com.android.server.am.AppBatteryExemptionTracker.DEFAULT_NAME; 34 import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX; 35 import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION; 36 37 import android.annotation.NonNull; 38 import android.annotation.Nullable; 39 import android.app.AppOpsManager; 40 import android.content.Context; 41 import android.content.pm.ApplicationInfo; 42 import android.content.pm.PackageManager.OnPermissionsChangedListener; 43 import android.content.pm.PackageManagerInternal; 44 import android.os.Handler; 45 import android.os.Message; 46 import android.os.Process; 47 import android.os.RemoteException; 48 import android.os.SystemClock; 49 import android.os.UserHandle; 50 import android.permission.PermissionManager; 51 import android.provider.DeviceConfig; 52 import android.text.TextUtils; 53 import android.util.ArraySet; 54 import android.util.Pair; 55 import android.util.Slog; 56 import android.util.SparseArray; 57 58 import com.android.internal.annotations.GuardedBy; 59 import com.android.internal.app.IAppOpsCallback; 60 import com.android.internal.app.IAppOpsService; 61 import com.android.server.am.AppPermissionTracker.AppPermissionPolicy; 62 import com.android.server.am.AppRestrictionController.TrackerType; 63 import com.android.server.pm.permission.PermissionManagerServiceInternal; 64 65 import java.io.PrintWriter; 66 import java.lang.reflect.Constructor; 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.List; 70 import java.util.Objects; 71 72 /** 73 * The tracker for monitoring selected permission state of apps. 74 */ 75 final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy> 76 implements OnPermissionsChangedListener { 77 static final String TAG = TAG_WITH_CLASS_NAME ? "AppPermissionTracker" : TAG_AM; 78 79 static final boolean DEBUG_PERMISSION_TRACKER = false; 80 81 private final MyHandler mHandler; 82 83 /** 84 * Keep a new instance of callback for each appop we're monitoring, 85 * as the AppOpsService doesn't support monitoring multiple appops with single callback 86 * instance (except the ALL_OPS case). 87 */ 88 @GuardedBy("mAppOpsCallbacks") 89 private final SparseArray<MyAppOpsCallback> mAppOpsCallbacks = new SparseArray<>(); 90 91 @GuardedBy("mLock") 92 private SparseArray<ArraySet<UidGrantedPermissionState>> mUidGrantedPermissionsInMonitor = 93 new SparseArray<>(); 94 95 private volatile boolean mLockedBootCompleted = false; 96 AppPermissionTracker(Context context, AppRestrictionController controller)97 AppPermissionTracker(Context context, AppRestrictionController controller) { 98 this(context, controller, null, null); 99 } 100 AppPermissionTracker(Context context, AppRestrictionController controller, Constructor<? extends Injector<AppPermissionPolicy>> injector, Object outerContext)101 AppPermissionTracker(Context context, AppRestrictionController controller, 102 Constructor<? extends Injector<AppPermissionPolicy>> injector, Object outerContext) { 103 super(context, controller, injector, outerContext); 104 mHandler = new MyHandler(this); 105 mInjector.setPolicy(new AppPermissionPolicy(mInjector, this)); 106 } 107 108 @Override getType()109 @TrackerType int getType() { 110 return AppRestrictionController.TRACKER_TYPE_PERMISSION; 111 } 112 113 @Override onPermissionsChanged(int uid)114 public void onPermissionsChanged(int uid) { 115 mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget(); 116 } 117 handleAppOpsInit()118 private void handleAppOpsInit() { 119 final ArrayList<Integer> ops = new ArrayList<>(); 120 final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); 121 for (int i = 0; i < permissions.length; i++) { 122 final Pair<String, Integer> pair = permissions[i]; 123 if (pair.second != OP_NONE) { 124 ops.add(pair.second); 125 } 126 } 127 startWatchingMode(ops.toArray(new Integer[ops.size()])); 128 } 129 handlePermissionsInit()130 private void handlePermissionsInit() { 131 final int[] allUsers = mInjector.getUserManagerInternal().getUserIds(); 132 final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); 133 final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal(); 134 final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); 135 final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = 136 mUidGrantedPermissionsInMonitor; 137 for (int userId : allUsers) { 138 final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID); 139 if (apps == null) { 140 continue; 141 } 142 final long now = SystemClock.elapsedRealtime(); 143 for (int i = 0, size = apps.size(); i < size; i++) { 144 final ApplicationInfo ai = apps.get(i); 145 for (Pair<String, Integer> permission : permissions) { 146 final UidGrantedPermissionState state = new UidGrantedPermissionState( 147 ai.uid, permission.first, permission.second); 148 if (!state.isGranted()) { 149 // No need to track it. 150 continue; 151 } 152 synchronized (mLock) { 153 ArraySet<UidGrantedPermissionState> grantedPermissions = 154 uidPerms.get(ai.uid); 155 if (grantedPermissions == null) { 156 grantedPermissions = new ArraySet<UidGrantedPermissionState>(); 157 uidPerms.put(ai.uid, grantedPermissions); 158 // This UID has at least one active permission-in-interest now, 159 // let the listeners know. 160 notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now, 161 STATE_TYPE_PERMISSION); 162 } 163 grantedPermissions.add(state); 164 } 165 } 166 } 167 } 168 } 169 handleAppOpsDestroy()170 private void handleAppOpsDestroy() { 171 stopWatchingMode(); 172 } 173 handlePermissionsDestroy()174 private void handlePermissionsDestroy() { 175 synchronized (mLock) { 176 final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = 177 mUidGrantedPermissionsInMonitor; 178 final long now = SystemClock.elapsedRealtime(); 179 for (int i = 0, size = uidPerms.size(); i < size; i++) { 180 final int uid = uidPerms.keyAt(i); 181 final ArraySet<UidGrantedPermissionState> grantedPermissions = uidPerms.valueAt(i); 182 if (grantedPermissions.size() > 0) { 183 notifyListenersOnStateChange(uid, DEFAULT_NAME, false, now, 184 STATE_TYPE_PERMISSION); 185 } 186 } 187 uidPerms.clear(); 188 } 189 } 190 handleOpChanged(int op, int uid, String packageName)191 private void handleOpChanged(int op, int uid, String packageName) { 192 if (DEBUG_PERMISSION_TRACKER) { 193 final IAppOpsService appOpsService = mInjector.getIAppOpsService(); 194 try { 195 final int mode = appOpsService.checkOperation(op, uid, packageName); 196 Slog.i(TAG, "onOpChanged: " + opToPublicName(op) 197 + " " + UserHandle.formatUid(uid) 198 + " " + packageName + " " + mode); 199 } catch (RemoteException e) { 200 // Intra-process call, should never happen. 201 } 202 } 203 final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); 204 if (permissions != null && permissions.length > 0) { 205 for (int i = 0; i < permissions.length; i++) { 206 final Pair<String, Integer> pair = permissions[i]; 207 if (pair.second != op) { 208 continue; 209 } 210 final UidGrantedPermissionState state = 211 new UidGrantedPermissionState(uid, pair.first, op); 212 synchronized (mLock) { 213 handlePermissionsChangedLocked(uid, new UidGrantedPermissionState[] {state}); 214 } 215 break; 216 } 217 } 218 } 219 handlePermissionsChanged(int uid)220 private void handlePermissionsChanged(int uid) { 221 if (DEBUG_PERMISSION_TRACKER) { 222 Slog.i(TAG, "handlePermissionsChanged " + UserHandle.formatUid(uid)); 223 } 224 final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); 225 if (permissions != null && permissions.length > 0) { 226 final PermissionManagerServiceInternal pm = 227 mInjector.getPermissionManagerServiceInternal(); 228 final UidGrantedPermissionState[] states = 229 new UidGrantedPermissionState[permissions.length]; 230 for (int i = 0; i < permissions.length; i++) { 231 final Pair<String, Integer> pair = permissions[i]; 232 states[i] = new UidGrantedPermissionState(uid, pair.first, pair.second); 233 if (DEBUG_PERMISSION_TRACKER) { 234 Slog.i(TAG, states[i].toString()); 235 } 236 } 237 synchronized (mLock) { 238 handlePermissionsChangedLocked(uid, states); 239 } 240 } 241 } 242 243 @GuardedBy("mLock") handlePermissionsChangedLocked(int uid, UidGrantedPermissionState[] states)244 private void handlePermissionsChangedLocked(int uid, UidGrantedPermissionState[] states) { 245 final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid); 246 ArraySet<UidGrantedPermissionState> grantedPermissions = index >= 0 247 ? mUidGrantedPermissionsInMonitor.valueAt(index) : null; 248 final long now = SystemClock.elapsedRealtime(); 249 for (int i = 0; i < states.length; i++) { 250 final boolean granted = states[i].isGranted(); 251 boolean changed = false; 252 if (granted) { 253 if (grantedPermissions == null) { 254 grantedPermissions = new ArraySet<>(); 255 mUidGrantedPermissionsInMonitor.put(uid, grantedPermissions); 256 changed = true; 257 } 258 grantedPermissions.add(states[i]); 259 } else if (grantedPermissions != null && !grantedPermissions.isEmpty()) { 260 if (grantedPermissions.remove(states[i]) && grantedPermissions.isEmpty()) { 261 mUidGrantedPermissionsInMonitor.removeAt(index); 262 changed = true; 263 } 264 } 265 if (changed) { 266 notifyListenersOnStateChange(uid, DEFAULT_NAME, granted, now, 267 STATE_TYPE_PERMISSION); 268 } 269 } 270 } 271 272 /** 273 * Represents the grant state of a permission + appop of the given UID. 274 */ 275 private class UidGrantedPermissionState { 276 final int mUid; 277 final @Nullable String mPermission; 278 final int mAppOp; 279 280 private boolean mPermissionGranted; 281 private boolean mAppOpAllowed; 282 UidGrantedPermissionState(int uid, @Nullable String permission, int appOp)283 UidGrantedPermissionState(int uid, @Nullable String permission, int appOp) { 284 mUid = uid; 285 mPermission = permission; 286 mAppOp = appOp; 287 updatePermissionState(); 288 updateAppOps(); 289 } 290 updatePermissionState()291 void updatePermissionState() { 292 if (TextUtils.isEmpty(mPermission)) { 293 mPermissionGranted = true; 294 return; 295 } 296 mPermissionGranted = mInjector.checkPermission(mPermission, Process.INVALID_PID, mUid) 297 == PERMISSION_GRANTED; 298 } 299 updateAppOps()300 void updateAppOps() { 301 if (mAppOp == OP_NONE) { 302 mAppOpAllowed = true; 303 return; 304 } 305 final String[] packages = mInjector.getPackageManager().getPackagesForUid(mUid); 306 if (packages != null) { 307 final IAppOpsService appOpsService = mInjector.getIAppOpsService(); 308 for (String pkg : packages) { 309 try { 310 final int mode = appOpsService.checkOperation(mAppOp, mUid, pkg); 311 if (mode == AppOpsManager.MODE_ALLOWED) { 312 mAppOpAllowed = true; 313 return; 314 } 315 } catch (RemoteException e) { 316 // Intra-process call, should never happen. 317 } 318 } 319 } 320 mAppOpAllowed = false; 321 } 322 isGranted()323 boolean isGranted() { 324 return mPermissionGranted && mAppOpAllowed; 325 } 326 327 @Override equals(Object other)328 public boolean equals(Object other) { 329 if (other == null || !(other instanceof UidGrantedPermissionState)) { 330 return false; 331 } 332 final UidGrantedPermissionState otherState = (UidGrantedPermissionState) other; 333 return mUid == otherState.mUid && mAppOp == otherState.mAppOp 334 && Objects.equals(mPermission, otherState.mPermission); 335 } 336 337 @Override hashCode()338 public int hashCode() { 339 return (Integer.hashCode(mUid) * 31 + Integer.hashCode(mAppOp)) * 31 340 + (mPermission == null ? 0 : mPermission.hashCode()); 341 } 342 343 @Override toString()344 public String toString() { 345 String s = "UidGrantedPermissionState{" 346 + System.identityHashCode(this) + " " 347 + UserHandle.formatUid(mUid) + ": "; 348 final boolean emptyPermissionName = TextUtils.isEmpty(mPermission); 349 if (!emptyPermissionName) { 350 s += mPermission + "=" + mPermissionGranted; 351 } 352 if (mAppOp != OP_NONE) { 353 if (!emptyPermissionName) { 354 s += ","; 355 } 356 s += opToPublicName(mAppOp) + "=" + mAppOpAllowed; 357 } 358 s += "}"; 359 return s; 360 } 361 } 362 startWatchingMode(@onNull Integer[] ops)363 private void startWatchingMode(@NonNull Integer[] ops) { 364 synchronized (mAppOpsCallbacks) { 365 stopWatchingMode(); 366 final IAppOpsService appOpsService = mInjector.getIAppOpsService(); 367 try { 368 for (int op: ops) { 369 final MyAppOpsCallback cb = new MyAppOpsCallback(); 370 mAppOpsCallbacks.put(op, cb); 371 appOpsService.startWatchingModeWithFlags(op, null, 372 AppOpsManager.WATCH_FOREGROUND_CHANGES, cb); 373 } 374 } catch (RemoteException e) { 375 // Intra-process call, should never happen. 376 } 377 } 378 } 379 stopWatchingMode()380 private void stopWatchingMode() { 381 synchronized (mAppOpsCallbacks) { 382 final IAppOpsService appOpsService = mInjector.getIAppOpsService(); 383 for (int i = mAppOpsCallbacks.size() - 1; i >= 0; i--) { 384 try { 385 appOpsService.stopWatchingMode(mAppOpsCallbacks.valueAt(i)); 386 } catch (RemoteException e) { 387 // Intra-process call, should never happen. 388 } 389 } 390 mAppOpsCallbacks.clear(); 391 } 392 } 393 394 private class MyAppOpsCallback extends IAppOpsCallback.Stub { 395 @Override opChanged(int op, int uid, String packageName, String persistentDeviceId)396 public void opChanged(int op, int uid, String packageName, String persistentDeviceId) { 397 if (uid < 0) return; 398 mHandler.obtainMessage(MyHandler.MSG_APPOPS_CHANGED, op, uid, packageName) 399 .sendToTarget(); 400 } 401 } 402 403 private static class MyHandler extends Handler { 404 static final int MSG_PERMISSIONS_INIT = 0; 405 static final int MSG_PERMISSIONS_DESTROY = 1; 406 static final int MSG_PERMISSIONS_CHANGED = 2; 407 static final int MSG_APPOPS_CHANGED = 3; 408 409 private @NonNull AppPermissionTracker mTracker; 410 MyHandler(@onNull AppPermissionTracker tracker)411 MyHandler(@NonNull AppPermissionTracker tracker) { 412 super(tracker.mBgHandler.getLooper()); 413 mTracker = tracker; 414 } 415 416 @Override handleMessage(Message msg)417 public void handleMessage(Message msg) { 418 switch (msg.what) { 419 case MSG_PERMISSIONS_INIT: 420 mTracker.handleAppOpsInit(); 421 mTracker.handlePermissionsInit(); 422 break; 423 case MSG_PERMISSIONS_DESTROY: 424 mTracker.handlePermissionsDestroy(); 425 mTracker.handleAppOpsDestroy(); 426 break; 427 case MSG_PERMISSIONS_CHANGED: 428 mTracker.handlePermissionsChanged(msg.arg1); 429 break; 430 case MSG_APPOPS_CHANGED: 431 mTracker.handleOpChanged(msg.arg1, msg.arg2, (String) msg.obj); 432 break; 433 } 434 } 435 } 436 onPermissionTrackerEnabled(boolean enabled)437 private void onPermissionTrackerEnabled(boolean enabled) { 438 if (!mLockedBootCompleted) { 439 // Not ready, bail out. 440 return; 441 } 442 final PermissionManager pm = mInjector.getPermissionManager(); 443 if (enabled) { 444 pm.addOnPermissionsChangeListener(this); 445 mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_INIT).sendToTarget(); 446 } else { 447 pm.removeOnPermissionsChangeListener(this); 448 mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_DESTROY).sendToTarget(); 449 } 450 } 451 452 @Override onLockedBootCompleted()453 void onLockedBootCompleted() { 454 mLockedBootCompleted = true; 455 onPermissionTrackerEnabled(mInjector.getPolicy().isEnabled()); 456 } 457 458 @Override dump(PrintWriter pw, String prefix)459 void dump(PrintWriter pw, String prefix) { 460 pw.print(prefix); 461 pw.println("APP PERMISSIONS TRACKER:"); 462 final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); 463 final String prefixMore = " " + prefix; 464 final String prefixMoreMore = " " + prefixMore; 465 for (Pair<String, Integer> permission : permissions) { 466 pw.print(prefixMore); 467 final boolean emptyPermissionName = TextUtils.isEmpty(permission.first); 468 if (!emptyPermissionName) { 469 pw.print(permission.first); 470 } 471 if (permission.second != OP_NONE) { 472 if (!emptyPermissionName) { 473 pw.print('+'); 474 } 475 pw.print(opToPublicName(permission.second)); 476 } 477 pw.println(':'); 478 synchronized (mLock) { 479 final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = 480 mUidGrantedPermissionsInMonitor; 481 pw.print(prefixMoreMore); 482 pw.print('['); 483 boolean needDelimiter = false; 484 for (int i = 0, size = uidPerms.size(); i < size; i++) { 485 final ArraySet<UidGrantedPermissionState> uidPerm = uidPerms.valueAt(i); 486 for (int j = uidPerm.size() - 1; j >= 0; j--) { 487 final UidGrantedPermissionState state = uidPerm.valueAt(j); 488 if (state.mAppOp == permission.second 489 && TextUtils.equals(state.mPermission, permission.first)) { 490 if (needDelimiter) { 491 pw.print(','); 492 } 493 needDelimiter = true; 494 pw.print(UserHandle.formatUid(state.mUid)); 495 break; 496 } 497 } 498 } 499 pw.println(']'); 500 } 501 } 502 super.dump(pw, prefix); 503 } 504 505 static final class AppPermissionPolicy extends BaseAppStatePolicy<AppPermissionTracker> { 506 /** 507 * Whether or not we should enable the monitoring on app permissions. 508 */ 509 static final String KEY_BG_PERMISSION_MONITOR_ENABLED = 510 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_monitor_enabled"; 511 512 /** 513 * The names of the permissions we're monitoring its changes. 514 */ 515 static final String KEY_BG_PERMISSIONS_IN_MONITOR = 516 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_in_monitor"; 517 518 /** 519 * Default value to {@link #mTrackerEnabled}. 520 */ 521 static final boolean DEFAULT_BG_PERMISSION_MONITOR_ENABLED = true; 522 523 /** 524 * Default value to {@link #mBgPermissionsInMonitor}, it comes in pair; 525 * the first string strings in the pair is the permission name, and the second string 526 * is the appops name, if they are associated. 527 */ 528 static final String[] DEFAULT_BG_PERMISSIONS_IN_MONITOR = new String[] { 529 ACCESS_FINE_LOCATION, OPSTR_FINE_LOCATION, 530 CAMERA, OPSTR_CAMERA, 531 RECORD_AUDIO, OPSTR_RECORD_AUDIO, 532 }; 533 534 /** 535 * @see #KEY_BG_PERMISSIONS_IN_MONITOR. 536 */ 537 volatile @NonNull Pair[] mBgPermissionsInMonitor; 538 AppPermissionPolicy(@onNull Injector injector, @NonNull AppPermissionTracker tracker)539 AppPermissionPolicy(@NonNull Injector injector, @NonNull AppPermissionTracker tracker) { 540 super(injector, tracker, KEY_BG_PERMISSION_MONITOR_ENABLED, 541 DEFAULT_BG_PERMISSION_MONITOR_ENABLED); 542 mBgPermissionsInMonitor = parsePermissionConfig(DEFAULT_BG_PERMISSIONS_IN_MONITOR); 543 } 544 545 @Override onSystemReady()546 public void onSystemReady() { 547 super.onSystemReady(); 548 updateBgPermissionsInMonitor(); 549 } 550 551 @Override onPropertiesChanged(String name)552 public void onPropertiesChanged(String name) { 553 switch (name) { 554 case KEY_BG_PERMISSIONS_IN_MONITOR: 555 updateBgPermissionsInMonitor(); 556 break; 557 default: 558 super.onPropertiesChanged(name); 559 break; 560 } 561 } 562 getBgPermissionsInMonitor()563 Pair[] getBgPermissionsInMonitor() { 564 return mBgPermissionsInMonitor; 565 } 566 parsePermissionConfig(@onNull String[] perms)567 private @NonNull Pair[] parsePermissionConfig(@NonNull String[] perms) { 568 final Pair[] result = new Pair[perms.length / 2]; 569 for (int i = 0, j = 0; i < perms.length; i += 2, j++) { 570 try { 571 result[j] = Pair.create(TextUtils.isEmpty(perms[i]) ? null : perms[i], 572 TextUtils.isEmpty(perms[i + 1]) ? OP_NONE : strOpToOp(perms[i + 1])); 573 } catch (Exception e) { 574 // Ignore. 575 } 576 } 577 return result; 578 } 579 updateBgPermissionsInMonitor()580 private void updateBgPermissionsInMonitor() { 581 final String config = DeviceConfig.getString( 582 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 583 KEY_BG_PERMISSIONS_IN_MONITOR, 584 null); 585 final Pair[] newPermsInMonitor = parsePermissionConfig( 586 config != null ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR); 587 if (!Arrays.equals(mBgPermissionsInMonitor, newPermsInMonitor)) { 588 mBgPermissionsInMonitor = newPermsInMonitor; 589 if (isEnabled()) { 590 // Trigger a reload. 591 onTrackerEnabled(false); 592 onTrackerEnabled(true); 593 } 594 } 595 } 596 597 @Override onTrackerEnabled(boolean enabled)598 public void onTrackerEnabled(boolean enabled) { 599 mTracker.onPermissionTrackerEnabled(enabled); 600 } 601 602 @Override dump(PrintWriter pw, String prefix)603 void dump(PrintWriter pw, String prefix) { 604 pw.print(prefix); 605 pw.println("APP PERMISSION TRACKER POLICY SETTINGS:"); 606 prefix = " " + prefix; 607 super.dump(pw, prefix); 608 pw.print(prefix); 609 pw.print(KEY_BG_PERMISSIONS_IN_MONITOR); 610 pw.print('='); 611 pw.print('['); 612 for (int i = 0; i < mBgPermissionsInMonitor.length; i++) { 613 if (i > 0) { 614 pw.print(','); 615 } 616 final Pair<String, Integer> pair = mBgPermissionsInMonitor[i]; 617 if (pair.first != null) { 618 pw.print(pair.first); 619 } 620 pw.print(','); 621 if (pair.second != OP_NONE) { 622 pw.print(opToPublicName(pair.second)); 623 } 624 } 625 pw.println(']'); 626 } 627 } 628 } 629