• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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