Loading core/java/com/android/internal/util/ArrayUtils.java +7 −0 Original line number Diff line number Diff line Loading @@ -161,6 +161,13 @@ public class ArrayUtils { return array == null || array.length == 0; } /** * Checks if given array is null or has zero elements. */ public static boolean isEmpty(@Nullable boolean[] array) { return array == null || array.length == 0; } /** * Checks that value is present as at least one of the elements of the array. * @param array the array to check in Loading services/core/java/com/android/server/AppOpsService.java +160 −188 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; Loading Loading @@ -57,7 +58,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; Loading Loading @@ -110,21 +110,8 @@ public class AppOpsService extends IAppOpsService.Stub { /* * These are app op restrictions imposed per user from various parties. * * This is organized as follows: * * ArrayMap w/ mapping: * IBinder (for client imposing restriction) --> SparseArray w/ mapping: * User handle --> Pair containing: * - Array w/ index = AppOp code, value = restricted status boolean * - SparseArray w/ mapping: * AppOp code --> Set of packages that are not restricted for this code * * For efficiency, a core assumption here is that the number of per-package exemptions stored * here will be relatively small. If this changes, this data structure should be revisited. */ private final ArrayMap<IBinder, SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>> mOpUserRestrictions = new ArrayMap<>(); private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>(); private static final class UidState { public final int uid; Loading Loading @@ -1328,33 +1315,9 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < restrictionSetCount; i++) { // For each client, check that the given op is not restricted, or that the given // package is exempt from the restriction. SparseArray<Pair<boolean[],SparseArray<ArraySet<String>>>> perUserRestrictions = mOpUserRestrictions.valueAt(i); Pair<boolean[],SparseArray<ArraySet<String>>> restrictions = perUserRestrictions.get(userHandle); if (restrictions == null) { continue; // No restrictions set by this client } boolean[] opRestrictions = restrictions.first; SparseArray<ArraySet<String>> opExceptions = restrictions.second; if (opRestrictions == null) { continue; // No restrictions set by this client } if (opRestrictions[code]) { if (opExceptions != null) { ArraySet<String> ex = opExceptions.get(code); if (ex != null && ex.contains(packageName)) { continue; // AppOps code is restricted, but this package is exempt } } if (AppOpsManager.opAllowSystemBypassRestriction(code)) { ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); if (restrictionState.hasRestriction(code, packageName, userHandle) && AppOpsManager.opAllowSystemBypassRestriction(code)) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { Ops ops = getOpsRawLocked(uid, packageName, true); Loading @@ -1362,8 +1325,6 @@ public class AppOpsService extends IAppOpsService.Stub { return false; } } } return true; } } Loading Loading @@ -2216,12 +2177,11 @@ public class AppOpsService extends IAppOpsService.Stub { checkSystemUid("setUserRestrictions"); Preconditions.checkNotNull(restrictions); Preconditions.checkNotNull(token); final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); for (int i = 0; i < opRestrictions.length; ++i) { for (int i = 0; i < AppOpsManager._NUM_OP; i++) { String restriction = AppOpsManager.opToRestriction(i); final boolean restricted = restriction != null && restrictions.getBoolean(restriction, false); setUserRestrictionNoCheck(i, restricted, token, userHandle); if (restriction != null && restrictions.getBoolean(restriction, false)) { setUserRestrictionNoCheck(i, true, token, userHandle, null); } } } Loading @@ -2246,48 +2206,29 @@ public class AppOpsService extends IAppOpsService.Stub { setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages); } private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle) { setUserRestrictionNoCheck(code, restricted, token, userHandle, /*exceptionPackages*/null); } private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle, String[] exceptionPackages) { ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); if (restricted) { final SparseArray<ArraySet<String>> opExceptions = getUserPackageExemptionsForToken(token, userHandle); ArraySet<String> exceptions = opExceptions.get(code); if (exceptionPackages != null && exceptionPackages.length > 0) { if (exceptions == null) { exceptions = new ArraySet<>(exceptionPackages.length); opExceptions.put(code, exceptions); } else { exceptions.clear(); } for (String p : exceptionPackages) { exceptions.add(p); } } else { opExceptions.remove(code); } } if (opRestrictions[code] == restricted) { if (restrictionState == null) { try { restrictionState = new ClientRestrictionState(token); } catch (RemoteException e) { return; } opRestrictions[code] = restricted; if (!restricted) { pruneUserRestrictionsForToken(token, userHandle); mOpUserRestrictions.put(token, restrictionState); } if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) { notifyWatchersOfChange(code); } if (restrictionState.isDefault()) { mOpUserRestrictions.remove(token); restrictionState.destroy(); } } private void notifyWatchersOfChange(int code) { final ArrayList<Callback> clonedCallbacks; synchronized (this) { Loading @@ -2300,7 +2241,7 @@ public class AppOpsService extends IAppOpsService.Stub { // There are components watching for mode changes such as window manager // and location manager which are in our process. The callbacks in these // components may require permissions our remote caller does not have. // components may require permissions our remote caller does not have.s final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = clonedCallbacks.size(); Loading @@ -2322,138 +2263,169 @@ public class AppOpsService extends IAppOpsService.Stub { checkSystemUid("removeUser"); final int tokenCount = mOpUserRestrictions.size(); for (int i = tokenCount - 1; i >= 0; i--) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> opRestrictions = mOpUserRestrictions.valueAt(i); if (opRestrictions != null) { opRestrictions.remove(userHandle); if (opRestrictions.size() <= 0) { mOpUserRestrictions.removeAt(i); ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i); opRestrictions.removeUser(userHandle); } } private void checkSystemUid(String function) { int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID) { throw new SecurityException(function + " must by called by the system"); } } private static String resolvePackageName(int uid, String packageName) { if (uid == 0) { return "root"; } else if (uid == Process.SHELL_UID) { return "com.android.shell"; } else if (uid == Process.SYSTEM_UID && packageName == null) { return "android"; } return packageName; } private void pruneUserRestrictionsForToken(IBinder token, int userHandle) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = mOpUserRestrictions.get(token); if (perTokenRestrictions != null) { final Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = perTokenRestrictions.get(userHandle); if (restrictions != null) { final boolean[] opRestrictions = restrictions.first; final SparseArray<ArraySet<String>> opExceptions = restrictions.second; boolean stillHasRestrictions = false; if (opRestrictions != null) { for (int i = 0; i < opRestrictions.length; i++) { boolean restriction = opRestrictions[i]; if (restriction) { stillHasRestrictions = true; } else { opExceptions.remove(i); private static String[] getPackagesForUid(int uid) { String[] packageNames = null; try { packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); } catch (RemoteException e) { /* ignore - local call */ } if (packageNames == null) { return EmptyArray.STRING; } return packageNames; } if (stillHasRestrictions) { return; private final class ClientRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; SparseArray<String[]> perUserExcludedPackages; public ClientRestrictionState(IBinder token) throws RemoteException { token.linkToDeath(this, 0); this.token = token; } // No restrictions set for this client perTokenRestrictions.remove(userHandle); if (perTokenRestrictions.size() <= 0) { mOpUserRestrictions.remove(token); public boolean setRestriction(int code, boolean restricted, String[] excludedPackages, int userId) { boolean changed = false; if (perUserRestrictions == null && restricted) { perUserRestrictions = new SparseArray<>(); } if (perUserRestrictions != null) { boolean[] userRestrictions = perUserRestrictions.get(userId); if (userRestrictions == null && restricted) { userRestrictions = new boolean[AppOpsManager._NUM_OP]; perUserRestrictions.put(userId, userRestrictions); } if (userRestrictions != null && userRestrictions[code] != restricted) { userRestrictions[code] = restricted; if (!restricted && isDefault(userRestrictions)) { perUserRestrictions.remove(userId); userRestrictions = null; } changed = true; } /** * Get or create the user restrictions array for a given client if it doesn't already exist. * * @param token the binder client creating the restriction. * @param userHandle the user handle to create a restriction for. * * @return the array of restriction states for each AppOps code. */ private boolean[] getOrCreateUserRestrictionsForToken(IBinder token, int userHandle) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = mOpUserRestrictions.get(token); if (perTokenRestrictions == null) { perTokenRestrictions = new SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>(); mOpUserRestrictions.put(token, perTokenRestrictions); if (userRestrictions != null) { final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackages); if (perUserExcludedPackages == null && !noExcludedPackages) { perUserExcludedPackages = new SparseArray<>(); } if (perUserExcludedPackages != null && !Arrays.equals(excludedPackages, perUserExcludedPackages.get(userId))) { if (noExcludedPackages) { perUserExcludedPackages.remove(userId); if (perUserExcludedPackages.size() <= 0) { perUserExcludedPackages = null; } } else { perUserExcludedPackages.put(userId, excludedPackages); } changed = true; } } } Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = perTokenRestrictions.get(userHandle); return changed; } public boolean hasRestriction(int restriction, String packageName, int userId) { if (perUserRestrictions == null) { return false; } boolean[] restrictions = perUserRestrictions.get(userId); if (restrictions == null) { restrictions = new Pair<boolean[], SparseArray<ArraySet<String>>>( new boolean[AppOpsManager._NUM_OP], new SparseArray<ArraySet<String>>()); perTokenRestrictions.put(userHandle, restrictions); return false; } return restrictions.first; if (!restrictions[restriction]) { return false; } /** * Get the per-package exemptions for each AppOps code for a given client and userHandle. * * @param token the binder client to get the exemptions for. * @param userHandle the user handle to get the exemptions for. * * @return a mapping from the AppOps code to a set of packages exempt for that code. */ private SparseArray<ArraySet<String>> getUserPackageExemptionsForToken(IBinder token, int userHandle) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = mOpUserRestrictions.get(token); if (perTokenRestrictions == null) { return null; // Don't create user restrictions accidentally if (perUserExcludedPackages == null) { return true; } String[] perUserExclusions = perUserExcludedPackages.get(userId); if (perUserExclusions == null) { return true; } return !ArrayUtils.contains(perUserExclusions, packageName); } Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = perTokenRestrictions.get(userHandle); if (restrictions == null) { return null; // Don't create user restrictions accidentally public void removeUser(int userId) { if (perUserExcludedPackages != null) { perUserExcludedPackages.remove(userId); if (perUserExcludedPackages.size() <= 0) { perUserExcludedPackages = null; } } } return restrictions.second; public boolean isDefault() { return perUserRestrictions == null || perUserRestrictions.size() <= 0; } private void checkSystemUid(String function) { int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID) { throw new SecurityException(function + " must by called by the system"); @Override public void binderDied() { synchronized (AppOpsService.this) { mOpUserRestrictions.remove(token); if (perUserRestrictions == null) { return; } final int userCount = perUserRestrictions.size(); for (int i = 0; i < userCount; i++) { final boolean[] restrictions = perUserRestrictions.valueAt(i); final int restrictionCount = restrictions.length; for (int j = 0; j < restrictionCount; j++) { if (restrictions[j]) { final int changedCode = j; mHandler.post(() -> notifyWatchersOfChange(changedCode)); } private static String resolvePackageName(int uid, String packageName) { if (uid == 0) { return "root"; } else if (uid == Process.SHELL_UID) { return "com.android.shell"; } else if (uid == Process.SYSTEM_UID && packageName == null) { return "android"; } return packageName; } destroy(); } } private static String[] getPackagesForUid(int uid) { String[] packageNames = null; try { packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); } catch (RemoteException e) { /* ignore - local call */ public void destroy() { token.unlinkToDeath(this, 0); } if (packageNames == null) { return EmptyArray.STRING; private boolean isDefault(boolean[] array) { if (ArrayUtils.isEmpty(array)) { return true; } for (boolean value : array) { if (value) { return false; } } return true; } return packageNames; } } Loading
core/java/com/android/internal/util/ArrayUtils.java +7 −0 Original line number Diff line number Diff line Loading @@ -161,6 +161,13 @@ public class ArrayUtils { return array == null || array.length == 0; } /** * Checks if given array is null or has zero elements. */ public static boolean isEmpty(@Nullable boolean[] array) { return array == null || array.length == 0; } /** * Checks that value is present as at least one of the elements of the array. * @param array the array to check in Loading
services/core/java/com/android/server/AppOpsService.java +160 −188 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; Loading Loading @@ -57,7 +58,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; Loading Loading @@ -110,21 +110,8 @@ public class AppOpsService extends IAppOpsService.Stub { /* * These are app op restrictions imposed per user from various parties. * * This is organized as follows: * * ArrayMap w/ mapping: * IBinder (for client imposing restriction) --> SparseArray w/ mapping: * User handle --> Pair containing: * - Array w/ index = AppOp code, value = restricted status boolean * - SparseArray w/ mapping: * AppOp code --> Set of packages that are not restricted for this code * * For efficiency, a core assumption here is that the number of per-package exemptions stored * here will be relatively small. If this changes, this data structure should be revisited. */ private final ArrayMap<IBinder, SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>> mOpUserRestrictions = new ArrayMap<>(); private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>(); private static final class UidState { public final int uid; Loading Loading @@ -1328,33 +1315,9 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < restrictionSetCount; i++) { // For each client, check that the given op is not restricted, or that the given // package is exempt from the restriction. SparseArray<Pair<boolean[],SparseArray<ArraySet<String>>>> perUserRestrictions = mOpUserRestrictions.valueAt(i); Pair<boolean[],SparseArray<ArraySet<String>>> restrictions = perUserRestrictions.get(userHandle); if (restrictions == null) { continue; // No restrictions set by this client } boolean[] opRestrictions = restrictions.first; SparseArray<ArraySet<String>> opExceptions = restrictions.second; if (opRestrictions == null) { continue; // No restrictions set by this client } if (opRestrictions[code]) { if (opExceptions != null) { ArraySet<String> ex = opExceptions.get(code); if (ex != null && ex.contains(packageName)) { continue; // AppOps code is restricted, but this package is exempt } } if (AppOpsManager.opAllowSystemBypassRestriction(code)) { ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); if (restrictionState.hasRestriction(code, packageName, userHandle) && AppOpsManager.opAllowSystemBypassRestriction(code)) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { Ops ops = getOpsRawLocked(uid, packageName, true); Loading @@ -1362,8 +1325,6 @@ public class AppOpsService extends IAppOpsService.Stub { return false; } } } return true; } } Loading Loading @@ -2216,12 +2177,11 @@ public class AppOpsService extends IAppOpsService.Stub { checkSystemUid("setUserRestrictions"); Preconditions.checkNotNull(restrictions); Preconditions.checkNotNull(token); final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); for (int i = 0; i < opRestrictions.length; ++i) { for (int i = 0; i < AppOpsManager._NUM_OP; i++) { String restriction = AppOpsManager.opToRestriction(i); final boolean restricted = restriction != null && restrictions.getBoolean(restriction, false); setUserRestrictionNoCheck(i, restricted, token, userHandle); if (restriction != null && restrictions.getBoolean(restriction, false)) { setUserRestrictionNoCheck(i, true, token, userHandle, null); } } } Loading @@ -2246,48 +2206,29 @@ public class AppOpsService extends IAppOpsService.Stub { setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages); } private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle) { setUserRestrictionNoCheck(code, restricted, token, userHandle, /*exceptionPackages*/null); } private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle, String[] exceptionPackages) { ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); if (restricted) { final SparseArray<ArraySet<String>> opExceptions = getUserPackageExemptionsForToken(token, userHandle); ArraySet<String> exceptions = opExceptions.get(code); if (exceptionPackages != null && exceptionPackages.length > 0) { if (exceptions == null) { exceptions = new ArraySet<>(exceptionPackages.length); opExceptions.put(code, exceptions); } else { exceptions.clear(); } for (String p : exceptionPackages) { exceptions.add(p); } } else { opExceptions.remove(code); } } if (opRestrictions[code] == restricted) { if (restrictionState == null) { try { restrictionState = new ClientRestrictionState(token); } catch (RemoteException e) { return; } opRestrictions[code] = restricted; if (!restricted) { pruneUserRestrictionsForToken(token, userHandle); mOpUserRestrictions.put(token, restrictionState); } if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) { notifyWatchersOfChange(code); } if (restrictionState.isDefault()) { mOpUserRestrictions.remove(token); restrictionState.destroy(); } } private void notifyWatchersOfChange(int code) { final ArrayList<Callback> clonedCallbacks; synchronized (this) { Loading @@ -2300,7 +2241,7 @@ public class AppOpsService extends IAppOpsService.Stub { // There are components watching for mode changes such as window manager // and location manager which are in our process. The callbacks in these // components may require permissions our remote caller does not have. // components may require permissions our remote caller does not have.s final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = clonedCallbacks.size(); Loading @@ -2322,138 +2263,169 @@ public class AppOpsService extends IAppOpsService.Stub { checkSystemUid("removeUser"); final int tokenCount = mOpUserRestrictions.size(); for (int i = tokenCount - 1; i >= 0; i--) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> opRestrictions = mOpUserRestrictions.valueAt(i); if (opRestrictions != null) { opRestrictions.remove(userHandle); if (opRestrictions.size() <= 0) { mOpUserRestrictions.removeAt(i); ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i); opRestrictions.removeUser(userHandle); } } private void checkSystemUid(String function) { int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID) { throw new SecurityException(function + " must by called by the system"); } } private static String resolvePackageName(int uid, String packageName) { if (uid == 0) { return "root"; } else if (uid == Process.SHELL_UID) { return "com.android.shell"; } else if (uid == Process.SYSTEM_UID && packageName == null) { return "android"; } return packageName; } private void pruneUserRestrictionsForToken(IBinder token, int userHandle) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = mOpUserRestrictions.get(token); if (perTokenRestrictions != null) { final Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = perTokenRestrictions.get(userHandle); if (restrictions != null) { final boolean[] opRestrictions = restrictions.first; final SparseArray<ArraySet<String>> opExceptions = restrictions.second; boolean stillHasRestrictions = false; if (opRestrictions != null) { for (int i = 0; i < opRestrictions.length; i++) { boolean restriction = opRestrictions[i]; if (restriction) { stillHasRestrictions = true; } else { opExceptions.remove(i); private static String[] getPackagesForUid(int uid) { String[] packageNames = null; try { packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); } catch (RemoteException e) { /* ignore - local call */ } if (packageNames == null) { return EmptyArray.STRING; } return packageNames; } if (stillHasRestrictions) { return; private final class ClientRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; SparseArray<String[]> perUserExcludedPackages; public ClientRestrictionState(IBinder token) throws RemoteException { token.linkToDeath(this, 0); this.token = token; } // No restrictions set for this client perTokenRestrictions.remove(userHandle); if (perTokenRestrictions.size() <= 0) { mOpUserRestrictions.remove(token); public boolean setRestriction(int code, boolean restricted, String[] excludedPackages, int userId) { boolean changed = false; if (perUserRestrictions == null && restricted) { perUserRestrictions = new SparseArray<>(); } if (perUserRestrictions != null) { boolean[] userRestrictions = perUserRestrictions.get(userId); if (userRestrictions == null && restricted) { userRestrictions = new boolean[AppOpsManager._NUM_OP]; perUserRestrictions.put(userId, userRestrictions); } if (userRestrictions != null && userRestrictions[code] != restricted) { userRestrictions[code] = restricted; if (!restricted && isDefault(userRestrictions)) { perUserRestrictions.remove(userId); userRestrictions = null; } changed = true; } /** * Get or create the user restrictions array for a given client if it doesn't already exist. * * @param token the binder client creating the restriction. * @param userHandle the user handle to create a restriction for. * * @return the array of restriction states for each AppOps code. */ private boolean[] getOrCreateUserRestrictionsForToken(IBinder token, int userHandle) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = mOpUserRestrictions.get(token); if (perTokenRestrictions == null) { perTokenRestrictions = new SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>(); mOpUserRestrictions.put(token, perTokenRestrictions); if (userRestrictions != null) { final boolean noExcludedPackages = ArrayUtils.isEmpty(excludedPackages); if (perUserExcludedPackages == null && !noExcludedPackages) { perUserExcludedPackages = new SparseArray<>(); } if (perUserExcludedPackages != null && !Arrays.equals(excludedPackages, perUserExcludedPackages.get(userId))) { if (noExcludedPackages) { perUserExcludedPackages.remove(userId); if (perUserExcludedPackages.size() <= 0) { perUserExcludedPackages = null; } } else { perUserExcludedPackages.put(userId, excludedPackages); } changed = true; } } } Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = perTokenRestrictions.get(userHandle); return changed; } public boolean hasRestriction(int restriction, String packageName, int userId) { if (perUserRestrictions == null) { return false; } boolean[] restrictions = perUserRestrictions.get(userId); if (restrictions == null) { restrictions = new Pair<boolean[], SparseArray<ArraySet<String>>>( new boolean[AppOpsManager._NUM_OP], new SparseArray<ArraySet<String>>()); perTokenRestrictions.put(userHandle, restrictions); return false; } return restrictions.first; if (!restrictions[restriction]) { return false; } /** * Get the per-package exemptions for each AppOps code for a given client and userHandle. * * @param token the binder client to get the exemptions for. * @param userHandle the user handle to get the exemptions for. * * @return a mapping from the AppOps code to a set of packages exempt for that code. */ private SparseArray<ArraySet<String>> getUserPackageExemptionsForToken(IBinder token, int userHandle) { SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = mOpUserRestrictions.get(token); if (perTokenRestrictions == null) { return null; // Don't create user restrictions accidentally if (perUserExcludedPackages == null) { return true; } String[] perUserExclusions = perUserExcludedPackages.get(userId); if (perUserExclusions == null) { return true; } return !ArrayUtils.contains(perUserExclusions, packageName); } Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = perTokenRestrictions.get(userHandle); if (restrictions == null) { return null; // Don't create user restrictions accidentally public void removeUser(int userId) { if (perUserExcludedPackages != null) { perUserExcludedPackages.remove(userId); if (perUserExcludedPackages.size() <= 0) { perUserExcludedPackages = null; } } } return restrictions.second; public boolean isDefault() { return perUserRestrictions == null || perUserRestrictions.size() <= 0; } private void checkSystemUid(String function) { int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID) { throw new SecurityException(function + " must by called by the system"); @Override public void binderDied() { synchronized (AppOpsService.this) { mOpUserRestrictions.remove(token); if (perUserRestrictions == null) { return; } final int userCount = perUserRestrictions.size(); for (int i = 0; i < userCount; i++) { final boolean[] restrictions = perUserRestrictions.valueAt(i); final int restrictionCount = restrictions.length; for (int j = 0; j < restrictionCount; j++) { if (restrictions[j]) { final int changedCode = j; mHandler.post(() -> notifyWatchersOfChange(changedCode)); } private static String resolvePackageName(int uid, String packageName) { if (uid == 0) { return "root"; } else if (uid == Process.SHELL_UID) { return "com.android.shell"; } else if (uid == Process.SYSTEM_UID && packageName == null) { return "android"; } return packageName; } destroy(); } } private static String[] getPackagesForUid(int uid) { String[] packageNames = null; try { packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); } catch (RemoteException e) { /* ignore - local call */ public void destroy() { token.unlinkToDeath(this, 0); } if (packageNames == null) { return EmptyArray.STRING; private boolean isDefault(boolean[] array) { if (ArrayUtils.isEmpty(array)) { return true; } for (boolean value : array) { if (value) { return false; } } return true; } return packageNames; } }