Loading services/core/java/com/android/server/appop/AppOpsRestrictions.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appop; import android.os.PackageTagsList; import java.io.PrintWriter; /** * Legacy implementation for AppOpsService's app-op restrictions (global and user) * storage and access. */ public interface AppOpsRestrictions { /** * Set or clear a global app-op restriction for the given {@code clientToken}. * * @param clientToken A token identifying the client this restriction applies to. * @param code The app-op opCode to set (or clear) a restriction for. * @param restricted {@code true} to restrict this app-op code, or {@code false} to clear an * existing restriction. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean setGlobalRestriction(Object clientToken, int code, boolean restricted); /** * Get the state of a global app-op restriction for the given {@code clientToken}. * * @param clientToken A token identifying the client to get the restriction state of. * @param code The app-op code to get the restriction state of. * @return the restriction state */ boolean getGlobalRestriction(Object clientToken, int code); /** * Returns {@code true} if *any* global app-op restrictions are currently set for the given * {@code clientToken}. * * @param clientToken A token identifying the client to check restrictions for. * @return {@code true} if any restrictions are set */ boolean hasGlobalRestrictions(Object clientToken); /** * Clear *all* global app-op restrictions for the given {@code clientToken}. * * @param clientToken A token identifying the client to clear restrictions from. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean clearGlobalRestrictions(Object clientToken); /** * Set or clear a user app-op restriction for the given {@code clientToken} and {@code userId}. * * @param clientToken A token identifying the client this restriction applies to. * @param code The app-op code to set (or clear) a restriction for. * @param restricted {@code true} to restrict this app-op code, or {@code false} to * remove any existing restriction. * @param excludedPackageTags A list of packages and associated attribution tags to exclude * from this restriction. Or, if {@code null}, removes any * exclusions from this restriction. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean setUserRestriction(Object clientToken, int userId, int code, boolean restricted, PackageTagsList excludedPackageTags); /** * Get the state of a user app-op restriction for the given {@code clientToken} and {@code * userId}. Or, if the combination of ({{@code clientToken}, {@code userId}, @code * packageName}, {@code attributionTag}) has been excluded via * {@link AppOpsRestrictions#setUserRestriction}, always returns {@code false}. * * @param clientToken A token identifying the client this restriction applies to. * @param userId Which userId this restriction applies to. * @param code The app-op code to get the restriction state of. * @param packageName A package name used to check for exclusions. * @param attributionTag An attribution tag used to check for exclusions. * @param isCheckOp a flag that, when {@code true}, denotes that exclusions should be * checked by (packageName) rather than (packageName, attributionTag) * @return the restriction state */ boolean getUserRestriction(Object clientToken, int userId, int code, String packageName, String attributionTag, boolean isCheckOp); /** * Returns {@code true} if *any* user app-op restrictions are currently set for the given * {@code clientToken}. * * @param clientToken A token identifying the client to check restrictions for. * @return {@code true} if any restrictions are set */ boolean hasUserRestrictions(Object clientToken); /** * Clear *all* user app-op restrictions for the given {@code clientToken}. * * @param clientToken A token identifying the client to clear restrictions for. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean clearUserRestrictions(Object clientToken); /** * Clear *all* user app-op restrictions for the given {@code clientToken} and {@code userId}. * * @param clientToken A token identifying the client to clear restrictions for. * @param userId Which userId to clear restrictions for. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean clearUserRestrictions(Object clientToken, Integer userId); /** * Returns the set of exclusions previously set by * {@link AppOpsRestrictions#setUserRestriction} for the given {@code clientToken} * and {@code userId}. * * @param clientToken A token identifying the client to get restriction exclusions for. * @param userId Which userId to get restriction exclusions for * @return a set of user restriction exclusions */ PackageTagsList getUserRestrictionExclusions(Object clientToken, int userId); /** * Dump the state of appop restrictions. * * @param printWriter writer to dump to. * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's * set to an app-op, only the watchers for that app-op are dumped. * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package * name. * @param showUserRestrictions include user restriction state in the output */ void dumpRestrictions(PrintWriter printWriter, int dumpOp, String dumpPackage, boolean showUserRestrictions); } services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java 0 → 100644 +452 −0 File added.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/appop/AppOpsService.java +20 −256 Original line number Diff line number Diff line Loading @@ -97,7 +97,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION; import android.net.Uri; Loading @@ -118,14 +117,12 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManagerInternal; import android.permission.PermissionManager; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; import android.util.Pair; import android.util.Slog; Loading Loading @@ -367,6 +364,9 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch /** Interface for app-op modes.*/ @VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface; /** Interface for app-op restrictions.*/ @VisibleForTesting AppOpsRestrictions mAppOpsRestrictions; private AppOpsUidStateTracker mUidStateTracker; /** Hands the definition of foreground and uid states */ Loading Loading @@ -926,6 +926,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch } mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this, handler, context, mSwitchedOps); mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler, mAppOpsServiceInterface); LockGuard.installLock(this, LockGuard.INDEX_APP_OPS); mFile = new AtomicFile(storagePath, "appops"); Loading Loading @@ -5348,124 +5350,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch pw.println(); } final int globalRestrictionCount = mOpGlobalRestrictions.size(); for (int i = 0; i < globalRestrictionCount; i++) { IBinder token = mOpGlobalRestrictions.keyAt(i); ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i); ArraySet<Integer> restrictedOps = restrictionState.mRestrictedOps; pw.println(" Global restrictions for token " + token + ":"); StringBuilder restrictedOpsValue = new StringBuilder(); restrictedOpsValue.append("["); final int restrictedOpCount = restrictedOps.size(); for (int j = 0; j < restrictedOpCount; j++) { if (restrictedOpsValue.length() > 1) { restrictedOpsValue.append(", "); } restrictedOpsValue.append(AppOpsManager.opToName(restrictedOps.valueAt(j))); } restrictedOpsValue.append("]"); pw.println(" Restricted ops: " + restrictedOpsValue); } final int userRestrictionCount = mOpUserRestrictions.size(); for (int i = 0; i < userRestrictionCount; i++) { IBinder token = mOpUserRestrictions.keyAt(i); ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); boolean printedTokenHeader = false; if (dumpMode >= 0 || dumpWatchers || dumpHistory) { continue; } final int restrictionCount = restrictionState.perUserRestrictions != null ? restrictionState.perUserRestrictions.size() : 0; if (restrictionCount > 0 && dumpPackage == null) { boolean printedOpsHeader = false; for (int j = 0; j < restrictionCount; j++) { int userId = restrictionState.perUserRestrictions.keyAt(j); boolean[] restrictedOps = restrictionState.perUserRestrictions.valueAt(j); if (restrictedOps == null) { continue; } if (dumpOp >= 0 && (dumpOp >= restrictedOps.length || !restrictedOps[dumpOp])) { continue; } if (!printedTokenHeader) { pw.println(" User restrictions for token " + token + ":"); printedTokenHeader = true; } if (!printedOpsHeader) { pw.println(" Restricted ops:"); printedOpsHeader = true; } StringBuilder restrictedOpsValue = new StringBuilder(); restrictedOpsValue.append("["); final int restrictedOpCount = restrictedOps.length; for (int k = 0; k < restrictedOpCount; k++) { if (restrictedOps[k]) { if (restrictedOpsValue.length() > 1) { restrictedOpsValue.append(", "); } restrictedOpsValue.append(AppOpsManager.opToName(k)); } } restrictedOpsValue.append("]"); pw.print(" "); pw.print("user: "); pw.print(userId); pw.print(" restricted ops: "); pw.println(restrictedOpsValue); } } final int excludedPackageCount = restrictionState.perUserExcludedPackageTags != null ? restrictionState.perUserExcludedPackageTags.size() : 0; if (excludedPackageCount > 0 && dumpOp < 0) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.increaseIndent(); boolean printedPackagesHeader = false; for (int j = 0; j < excludedPackageCount; j++) { int userId = restrictionState.perUserExcludedPackageTags.keyAt(j); PackageTagsList packageNames = restrictionState.perUserExcludedPackageTags.valueAt(j); if (packageNames == null) { continue; } boolean hasPackage; if (dumpPackage != null) { hasPackage = packageNames.includes(dumpPackage); } else { hasPackage = true; } if (!hasPackage) { continue; } if (!printedTokenHeader) { ipw.println("User restrictions for token " + token + ":"); printedTokenHeader = true; } ipw.increaseIndent(); if (!printedPackagesHeader) { ipw.println("Excluded packages:"); printedPackagesHeader = true; } ipw.increaseIndent(); ipw.print("user: "); ipw.print(userId); ipw.println(" packages: "); ipw.increaseIndent(); packageNames.dump(ipw); ipw.decreaseIndent(); ipw.decreaseIndent(); ipw.decreaseIndent(); } ipw.decreaseIndent(); } } boolean showUserRestrictions = !(dumpMode < 0 && !dumpWatchers && !dumpHistory); mAppOpsRestrictions.dumpRestrictions(pw, dumpOp, dumpPackage, showUserRestrictions); if (!dumpHistory && !dumpWatchers) { pw.println(); Loading Loading @@ -6085,8 +5971,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch private final class ClientUserRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; SparseArray<PackageTagsList> perUserExcludedPackageTags; ClientUserRestrictionState(IBinder token) throws RemoteException { Loading @@ -6096,134 +5980,29 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch public boolean setRestriction(int code, boolean restricted, PackageTagsList excludedPackageTags, int userId) { boolean changed = false; if (perUserRestrictions == null && restricted) { perUserRestrictions = new SparseArray<>(); } int[] users; if (userId == UserHandle.USER_ALL) { // TODO(b/162888972): this call is returning all users, not just live ones - we // need to either fix the method called, or rename the variable List<UserInfo> liveUsers = UserManager.get(mContext).getUsers(); users = new int[liveUsers.size()]; for (int i = 0; i < liveUsers.size(); i++) { users[i] = liveUsers.get(i).id; } } else { users = new int[]{userId}; return mAppOpsRestrictions.setUserRestriction(token, userId, code, restricted, excludedPackageTags); } if (perUserRestrictions != null) { int numUsers = users.length; for (int i = 0; i < numUsers; i++) { int thisUserId = users[i]; boolean[] userRestrictions = perUserRestrictions.get(thisUserId); if (userRestrictions == null && restricted) { userRestrictions = new boolean[AppOpsManager._NUM_OP]; perUserRestrictions.put(thisUserId, userRestrictions); } if (userRestrictions != null && userRestrictions[code] != restricted) { userRestrictions[code] = restricted; if (!restricted && isDefault(userRestrictions)) { perUserRestrictions.remove(thisUserId); userRestrictions = null; } changed = true; } if (userRestrictions != null) { final boolean noExcludedPackages = excludedPackageTags == null || excludedPackageTags.isEmpty(); if (perUserExcludedPackageTags == null && !noExcludedPackages) { perUserExcludedPackageTags = new SparseArray<>(); } if (perUserExcludedPackageTags != null) { if (noExcludedPackages) { perUserExcludedPackageTags.remove(thisUserId); if (perUserExcludedPackageTags.size() <= 0) { perUserExcludedPackageTags = null; } } else { perUserExcludedPackageTags.put(thisUserId, excludedPackageTags); } changed = true; } } } } return changed; } public boolean hasRestriction(int restriction, String packageName, String attributionTag, public boolean hasRestriction(int code, String packageName, String attributionTag, int userId, boolean isCheckOp) { if (perUserRestrictions == null) { return false; } boolean[] restrictions = perUserRestrictions.get(userId); if (restrictions == null) { return false; } if (!restrictions[restriction]) { return false; } if (perUserExcludedPackageTags == null) { return true; } PackageTagsList perUserExclusions = perUserExcludedPackageTags.get(userId); if (perUserExclusions == null) { return true; } // TODO (b/240617242) add overload for checkOp to support attribution tags if (isCheckOp) { return !perUserExclusions.includes(packageName); } return !perUserExclusions.contains(packageName, attributionTag); return mAppOpsRestrictions.getUserRestriction(token, userId, code, packageName, attributionTag, isCheckOp); } public void removeUser(int userId) { if (perUserExcludedPackageTags != null) { perUserExcludedPackageTags.remove(userId); if (perUserExcludedPackageTags.size() <= 0) { perUserExcludedPackageTags = null; } } if (perUserRestrictions != null) { perUserRestrictions.remove(userId); if (perUserRestrictions.size() <= 0) { perUserRestrictions = null; } } mAppOpsRestrictions.clearUserRestrictions(token, userId); } public boolean isDefault() { return perUserRestrictions == null || perUserRestrictions.size() <= 0; return !mAppOpsRestrictions.hasUserRestrictions(token); } @Override public void binderDied() { synchronized (AppOpsService.this) { mAppOpsRestrictions.clearUserRestrictions(token); 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, UID_ANY)); } } } destroy(); } } Loading @@ -6231,23 +6010,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch public void destroy() { token.unlinkToDeath(this, 0); } private boolean isDefault(boolean[] array) { if (ArrayUtils.isEmpty(array)) { return true; } for (boolean value : array) { if (value) { return false; } } return true; } } private final class ClientGlobalRestrictionState implements DeathRecipient { final IBinder mToken; final ArraySet<Integer> mRestrictedOps = new ArraySet<>(); ClientGlobalRestrictionState(IBinder token) throws RemoteException { Loading @@ -6256,23 +6022,21 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch } boolean setRestriction(int code, boolean restricted) { if (restricted) { return mRestrictedOps.add(code); } else { return mRestrictedOps.remove(code); } return mAppOpsRestrictions.setGlobalRestriction(mToken, code, restricted); } boolean hasRestriction(int code) { return mRestrictedOps.contains(code); return mAppOpsRestrictions.getGlobalRestriction(mToken, code); } boolean isDefault() { return mRestrictedOps.isEmpty(); return !mAppOpsRestrictions.hasGlobalRestrictions(mToken); } @Override public void binderDied() { mAppOpsRestrictions.clearGlobalRestrictions(mToken); mOpGlobalRestrictions.remove(mToken); destroy(); } Loading services/core/java/com/android/server/appop/AppOpsServiceInterface.java +8 −1 Original line number Diff line number Diff line Loading @@ -145,6 +145,14 @@ public interface AppOpsServiceInterface { */ ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Notify that the app-op's mode is changed by triggering the change listener. * @param op App-op whose mode has changed * @param uid user id associated with the app-op (or, if UID_ANY, notifies all users) */ void notifyWatchersOfChange(int op, int uid); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Notify that the app-op's mode is changed by triggering the change listener. Loading Loading @@ -198,5 +206,4 @@ public interface AppOpsServiceInterface { * @param printWriter writer to dump to. */ boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, PrintWriter printWriter); } services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java +12 −0 Original line number Diff line number Diff line Loading @@ -332,6 +332,18 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface } } @Override public void notifyWatchersOfChange(int code, int uid) { ArraySet<OnOpModeChangedListener> listenerSet = getOpModeChangedListeners(code); if (listenerSet == null) { return; } for (int i = 0; i < listenerSet.size(); i++) { final OnOpModeChangedListener listener = listenerSet.valueAt(i); notifyOpChanged(listener, code, uid, null); } } @Override public void notifyOpChanged(@NonNull OnOpModeChangedListener onModeChangedListener, int code, int uid, @Nullable String packageName) { Loading Loading
services/core/java/com/android/server/appop/AppOpsRestrictions.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appop; import android.os.PackageTagsList; import java.io.PrintWriter; /** * Legacy implementation for AppOpsService's app-op restrictions (global and user) * storage and access. */ public interface AppOpsRestrictions { /** * Set or clear a global app-op restriction for the given {@code clientToken}. * * @param clientToken A token identifying the client this restriction applies to. * @param code The app-op opCode to set (or clear) a restriction for. * @param restricted {@code true} to restrict this app-op code, or {@code false} to clear an * existing restriction. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean setGlobalRestriction(Object clientToken, int code, boolean restricted); /** * Get the state of a global app-op restriction for the given {@code clientToken}. * * @param clientToken A token identifying the client to get the restriction state of. * @param code The app-op code to get the restriction state of. * @return the restriction state */ boolean getGlobalRestriction(Object clientToken, int code); /** * Returns {@code true} if *any* global app-op restrictions are currently set for the given * {@code clientToken}. * * @param clientToken A token identifying the client to check restrictions for. * @return {@code true} if any restrictions are set */ boolean hasGlobalRestrictions(Object clientToken); /** * Clear *all* global app-op restrictions for the given {@code clientToken}. * * @param clientToken A token identifying the client to clear restrictions from. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean clearGlobalRestrictions(Object clientToken); /** * Set or clear a user app-op restriction for the given {@code clientToken} and {@code userId}. * * @param clientToken A token identifying the client this restriction applies to. * @param code The app-op code to set (or clear) a restriction for. * @param restricted {@code true} to restrict this app-op code, or {@code false} to * remove any existing restriction. * @param excludedPackageTags A list of packages and associated attribution tags to exclude * from this restriction. Or, if {@code null}, removes any * exclusions from this restriction. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean setUserRestriction(Object clientToken, int userId, int code, boolean restricted, PackageTagsList excludedPackageTags); /** * Get the state of a user app-op restriction for the given {@code clientToken} and {@code * userId}. Or, if the combination of ({{@code clientToken}, {@code userId}, @code * packageName}, {@code attributionTag}) has been excluded via * {@link AppOpsRestrictions#setUserRestriction}, always returns {@code false}. * * @param clientToken A token identifying the client this restriction applies to. * @param userId Which userId this restriction applies to. * @param code The app-op code to get the restriction state of. * @param packageName A package name used to check for exclusions. * @param attributionTag An attribution tag used to check for exclusions. * @param isCheckOp a flag that, when {@code true}, denotes that exclusions should be * checked by (packageName) rather than (packageName, attributionTag) * @return the restriction state */ boolean getUserRestriction(Object clientToken, int userId, int code, String packageName, String attributionTag, boolean isCheckOp); /** * Returns {@code true} if *any* user app-op restrictions are currently set for the given * {@code clientToken}. * * @param clientToken A token identifying the client to check restrictions for. * @return {@code true} if any restrictions are set */ boolean hasUserRestrictions(Object clientToken); /** * Clear *all* user app-op restrictions for the given {@code clientToken}. * * @param clientToken A token identifying the client to clear restrictions for. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean clearUserRestrictions(Object clientToken); /** * Clear *all* user app-op restrictions for the given {@code clientToken} and {@code userId}. * * @param clientToken A token identifying the client to clear restrictions for. * @param userId Which userId to clear restrictions for. * @return {@code true} if any restriction state was modified as a result of this operation */ boolean clearUserRestrictions(Object clientToken, Integer userId); /** * Returns the set of exclusions previously set by * {@link AppOpsRestrictions#setUserRestriction} for the given {@code clientToken} * and {@code userId}. * * @param clientToken A token identifying the client to get restriction exclusions for. * @param userId Which userId to get restriction exclusions for * @return a set of user restriction exclusions */ PackageTagsList getUserRestrictionExclusions(Object clientToken, int userId); /** * Dump the state of appop restrictions. * * @param printWriter writer to dump to. * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's * set to an app-op, only the watchers for that app-op are dumped. * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package * name. * @param showUserRestrictions include user restriction state in the output */ void dumpRestrictions(PrintWriter printWriter, int dumpOp, String dumpPackage, boolean showUserRestrictions); }
services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java 0 → 100644 +452 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/appop/AppOpsService.java +20 −256 Original line number Diff line number Diff line Loading @@ -97,7 +97,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION; import android.net.Uri; Loading @@ -118,14 +117,12 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManagerInternal; import android.permission.PermissionManager; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; import android.util.Pair; import android.util.Slog; Loading Loading @@ -367,6 +364,9 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch /** Interface for app-op modes.*/ @VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface; /** Interface for app-op restrictions.*/ @VisibleForTesting AppOpsRestrictions mAppOpsRestrictions; private AppOpsUidStateTracker mUidStateTracker; /** Hands the definition of foreground and uid states */ Loading Loading @@ -926,6 +926,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch } mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this, handler, context, mSwitchedOps); mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler, mAppOpsServiceInterface); LockGuard.installLock(this, LockGuard.INDEX_APP_OPS); mFile = new AtomicFile(storagePath, "appops"); Loading Loading @@ -5348,124 +5350,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch pw.println(); } final int globalRestrictionCount = mOpGlobalRestrictions.size(); for (int i = 0; i < globalRestrictionCount; i++) { IBinder token = mOpGlobalRestrictions.keyAt(i); ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i); ArraySet<Integer> restrictedOps = restrictionState.mRestrictedOps; pw.println(" Global restrictions for token " + token + ":"); StringBuilder restrictedOpsValue = new StringBuilder(); restrictedOpsValue.append("["); final int restrictedOpCount = restrictedOps.size(); for (int j = 0; j < restrictedOpCount; j++) { if (restrictedOpsValue.length() > 1) { restrictedOpsValue.append(", "); } restrictedOpsValue.append(AppOpsManager.opToName(restrictedOps.valueAt(j))); } restrictedOpsValue.append("]"); pw.println(" Restricted ops: " + restrictedOpsValue); } final int userRestrictionCount = mOpUserRestrictions.size(); for (int i = 0; i < userRestrictionCount; i++) { IBinder token = mOpUserRestrictions.keyAt(i); ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); boolean printedTokenHeader = false; if (dumpMode >= 0 || dumpWatchers || dumpHistory) { continue; } final int restrictionCount = restrictionState.perUserRestrictions != null ? restrictionState.perUserRestrictions.size() : 0; if (restrictionCount > 0 && dumpPackage == null) { boolean printedOpsHeader = false; for (int j = 0; j < restrictionCount; j++) { int userId = restrictionState.perUserRestrictions.keyAt(j); boolean[] restrictedOps = restrictionState.perUserRestrictions.valueAt(j); if (restrictedOps == null) { continue; } if (dumpOp >= 0 && (dumpOp >= restrictedOps.length || !restrictedOps[dumpOp])) { continue; } if (!printedTokenHeader) { pw.println(" User restrictions for token " + token + ":"); printedTokenHeader = true; } if (!printedOpsHeader) { pw.println(" Restricted ops:"); printedOpsHeader = true; } StringBuilder restrictedOpsValue = new StringBuilder(); restrictedOpsValue.append("["); final int restrictedOpCount = restrictedOps.length; for (int k = 0; k < restrictedOpCount; k++) { if (restrictedOps[k]) { if (restrictedOpsValue.length() > 1) { restrictedOpsValue.append(", "); } restrictedOpsValue.append(AppOpsManager.opToName(k)); } } restrictedOpsValue.append("]"); pw.print(" "); pw.print("user: "); pw.print(userId); pw.print(" restricted ops: "); pw.println(restrictedOpsValue); } } final int excludedPackageCount = restrictionState.perUserExcludedPackageTags != null ? restrictionState.perUserExcludedPackageTags.size() : 0; if (excludedPackageCount > 0 && dumpOp < 0) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.increaseIndent(); boolean printedPackagesHeader = false; for (int j = 0; j < excludedPackageCount; j++) { int userId = restrictionState.perUserExcludedPackageTags.keyAt(j); PackageTagsList packageNames = restrictionState.perUserExcludedPackageTags.valueAt(j); if (packageNames == null) { continue; } boolean hasPackage; if (dumpPackage != null) { hasPackage = packageNames.includes(dumpPackage); } else { hasPackage = true; } if (!hasPackage) { continue; } if (!printedTokenHeader) { ipw.println("User restrictions for token " + token + ":"); printedTokenHeader = true; } ipw.increaseIndent(); if (!printedPackagesHeader) { ipw.println("Excluded packages:"); printedPackagesHeader = true; } ipw.increaseIndent(); ipw.print("user: "); ipw.print(userId); ipw.println(" packages: "); ipw.increaseIndent(); packageNames.dump(ipw); ipw.decreaseIndent(); ipw.decreaseIndent(); ipw.decreaseIndent(); } ipw.decreaseIndent(); } } boolean showUserRestrictions = !(dumpMode < 0 && !dumpWatchers && !dumpHistory); mAppOpsRestrictions.dumpRestrictions(pw, dumpOp, dumpPackage, showUserRestrictions); if (!dumpHistory && !dumpWatchers) { pw.println(); Loading Loading @@ -6085,8 +5971,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch private final class ClientUserRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; SparseArray<PackageTagsList> perUserExcludedPackageTags; ClientUserRestrictionState(IBinder token) throws RemoteException { Loading @@ -6096,134 +5980,29 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch public boolean setRestriction(int code, boolean restricted, PackageTagsList excludedPackageTags, int userId) { boolean changed = false; if (perUserRestrictions == null && restricted) { perUserRestrictions = new SparseArray<>(); } int[] users; if (userId == UserHandle.USER_ALL) { // TODO(b/162888972): this call is returning all users, not just live ones - we // need to either fix the method called, or rename the variable List<UserInfo> liveUsers = UserManager.get(mContext).getUsers(); users = new int[liveUsers.size()]; for (int i = 0; i < liveUsers.size(); i++) { users[i] = liveUsers.get(i).id; } } else { users = new int[]{userId}; return mAppOpsRestrictions.setUserRestriction(token, userId, code, restricted, excludedPackageTags); } if (perUserRestrictions != null) { int numUsers = users.length; for (int i = 0; i < numUsers; i++) { int thisUserId = users[i]; boolean[] userRestrictions = perUserRestrictions.get(thisUserId); if (userRestrictions == null && restricted) { userRestrictions = new boolean[AppOpsManager._NUM_OP]; perUserRestrictions.put(thisUserId, userRestrictions); } if (userRestrictions != null && userRestrictions[code] != restricted) { userRestrictions[code] = restricted; if (!restricted && isDefault(userRestrictions)) { perUserRestrictions.remove(thisUserId); userRestrictions = null; } changed = true; } if (userRestrictions != null) { final boolean noExcludedPackages = excludedPackageTags == null || excludedPackageTags.isEmpty(); if (perUserExcludedPackageTags == null && !noExcludedPackages) { perUserExcludedPackageTags = new SparseArray<>(); } if (perUserExcludedPackageTags != null) { if (noExcludedPackages) { perUserExcludedPackageTags.remove(thisUserId); if (perUserExcludedPackageTags.size() <= 0) { perUserExcludedPackageTags = null; } } else { perUserExcludedPackageTags.put(thisUserId, excludedPackageTags); } changed = true; } } } } return changed; } public boolean hasRestriction(int restriction, String packageName, String attributionTag, public boolean hasRestriction(int code, String packageName, String attributionTag, int userId, boolean isCheckOp) { if (perUserRestrictions == null) { return false; } boolean[] restrictions = perUserRestrictions.get(userId); if (restrictions == null) { return false; } if (!restrictions[restriction]) { return false; } if (perUserExcludedPackageTags == null) { return true; } PackageTagsList perUserExclusions = perUserExcludedPackageTags.get(userId); if (perUserExclusions == null) { return true; } // TODO (b/240617242) add overload for checkOp to support attribution tags if (isCheckOp) { return !perUserExclusions.includes(packageName); } return !perUserExclusions.contains(packageName, attributionTag); return mAppOpsRestrictions.getUserRestriction(token, userId, code, packageName, attributionTag, isCheckOp); } public void removeUser(int userId) { if (perUserExcludedPackageTags != null) { perUserExcludedPackageTags.remove(userId); if (perUserExcludedPackageTags.size() <= 0) { perUserExcludedPackageTags = null; } } if (perUserRestrictions != null) { perUserRestrictions.remove(userId); if (perUserRestrictions.size() <= 0) { perUserRestrictions = null; } } mAppOpsRestrictions.clearUserRestrictions(token, userId); } public boolean isDefault() { return perUserRestrictions == null || perUserRestrictions.size() <= 0; return !mAppOpsRestrictions.hasUserRestrictions(token); } @Override public void binderDied() { synchronized (AppOpsService.this) { mAppOpsRestrictions.clearUserRestrictions(token); 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, UID_ANY)); } } } destroy(); } } Loading @@ -6231,23 +6010,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch public void destroy() { token.unlinkToDeath(this, 0); } private boolean isDefault(boolean[] array) { if (ArrayUtils.isEmpty(array)) { return true; } for (boolean value : array) { if (value) { return false; } } return true; } } private final class ClientGlobalRestrictionState implements DeathRecipient { final IBinder mToken; final ArraySet<Integer> mRestrictedOps = new ArraySet<>(); ClientGlobalRestrictionState(IBinder token) throws RemoteException { Loading @@ -6256,23 +6022,21 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch } boolean setRestriction(int code, boolean restricted) { if (restricted) { return mRestrictedOps.add(code); } else { return mRestrictedOps.remove(code); } return mAppOpsRestrictions.setGlobalRestriction(mToken, code, restricted); } boolean hasRestriction(int code) { return mRestrictedOps.contains(code); return mAppOpsRestrictions.getGlobalRestriction(mToken, code); } boolean isDefault() { return mRestrictedOps.isEmpty(); return !mAppOpsRestrictions.hasGlobalRestrictions(mToken); } @Override public void binderDied() { mAppOpsRestrictions.clearGlobalRestrictions(mToken); mOpGlobalRestrictions.remove(mToken); destroy(); } Loading
services/core/java/com/android/server/appop/AppOpsServiceInterface.java +8 −1 Original line number Diff line number Diff line Loading @@ -145,6 +145,14 @@ public interface AppOpsServiceInterface { */ ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Notify that the app-op's mode is changed by triggering the change listener. * @param op App-op whose mode has changed * @param uid user id associated with the app-op (or, if UID_ANY, notifies all users) */ void notifyWatchersOfChange(int op, int uid); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Notify that the app-op's mode is changed by triggering the change listener. Loading Loading @@ -198,5 +206,4 @@ public interface AppOpsServiceInterface { * @param printWriter writer to dump to. */ boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, PrintWriter printWriter); }
services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java +12 −0 Original line number Diff line number Diff line Loading @@ -332,6 +332,18 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface } } @Override public void notifyWatchersOfChange(int code, int uid) { ArraySet<OnOpModeChangedListener> listenerSet = getOpModeChangedListeners(code); if (listenerSet == null) { return; } for (int i = 0; i < listenerSet.size(); i++) { final OnOpModeChangedListener listener = listenerSet.valueAt(i); notifyOpChanged(listener, code, uid, null); } } @Override public void notifyOpChanged(@NonNull OnOpModeChangedListener onModeChangedListener, int code, int uid, @Nullable String packageName) { Loading