Merge "Allow exemption of roles/permissions from broadcast response events." into tm-dev

This commit is contained in:
Sudheer Shanka
2022-06-01 19:38:17 +00:00
committed by Android (Google) Code Review
4 changed files with 211 additions and 1 deletions

View File

@@ -280,6 +280,20 @@ public interface AppStandbyInternal {
*/ */
boolean shouldNoteResponseEventForAllBroadcastSessions(); boolean shouldNoteResponseEventForAllBroadcastSessions();
/**
* Returns the list of roles whose holders are exempted from the requirement of starting
* a response event after receiving a broadcast.
*/
@NonNull
List<String> getBroadcastResponseExemptedRoles();
/**
* Returns the list of permissions whose holders are exempted from the requirement of starting
* a response event after receiving a broadcast.
*/
@NonNull
List<String> getBroadcastResponseExemptedPermissions();
/** /**
* Return the last known value corresponding to the {@code key} from * Return the last known value corresponding to the {@code key} from
* {@link android.provider.DeviceConfig#NAMESPACE_APP_STANDBY} in AppStandbyController. * {@link android.provider.DeviceConfig#NAMESPACE_APP_STANDBY} in AppStandbyController.

View File

@@ -100,6 +100,7 @@ import android.os.UserHandle;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.provider.Settings.Global; import android.provider.Settings.Global;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter;
@@ -420,6 +421,26 @@ public class AppStandbyController
volatile boolean mNoteResponseEventForAllBroadcastSessions = volatile boolean mNoteResponseEventForAllBroadcastSessions =
ConstantsObserver.DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS; ConstantsObserver.DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS;
/**
* List of roles whose holders are exempted from the requirement of starting
* a response event after receiving a broadcast.
*
* The list of roles will be separated by '|' in the string.
*/
volatile String mBroadcastResponseExemptedRoles =
ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_EXEMPTED_ROLES;
volatile List<String> mBroadcastResponseExemptedRolesList = Collections.EMPTY_LIST;
/**
* List of permissions whose holders are exempted from the requirement of starting
* a response event after receiving a broadcast.
*
* The list of permissions will be separated by '|' in the string.
*/
volatile String mBroadcastResponseExemptedPermissions =
ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS;
volatile List<String> mBroadcastResponseExemptedPermissionsList = Collections.EMPTY_LIST;
/** /**
* Map of last known values of keys in {@link DeviceConfig#NAMESPACE_APP_STANDBY}. * Map of last known values of keys in {@link DeviceConfig#NAMESPACE_APP_STANDBY}.
* *
@@ -1959,6 +1980,18 @@ public class AppStandbyController
return mNoteResponseEventForAllBroadcastSessions; return mNoteResponseEventForAllBroadcastSessions;
} }
@Override
@NonNull
public List<String> getBroadcastResponseExemptedRoles() {
return mBroadcastResponseExemptedRolesList;
}
@Override
@NonNull
public List<String> getBroadcastResponseExemptedPermissions() {
return mBroadcastResponseExemptedPermissionsList;
}
@Override @Override
@Nullable @Nullable
public String getAppStandbyConstant(@NonNull String key) { public String getAppStandbyConstant(@NonNull String key) {
@@ -2311,6 +2344,14 @@ public class AppStandbyController
pw.print(mNoteResponseEventForAllBroadcastSessions); pw.print(mNoteResponseEventForAllBroadcastSessions);
pw.println(); pw.println();
pw.print(" mBroadcastResponseExemptedRoles");
pw.print(mBroadcastResponseExemptedRoles);
pw.println();
pw.print(" mBroadcastResponseExemptedPermissions");
pw.print(mBroadcastResponseExemptedPermissions);
pw.println();
pw.println(); pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
pw.print(" mAllowRestrictedBucket="); pw.print(" mAllowRestrictedBucket=");
@@ -2795,6 +2836,10 @@ public class AppStandbyController
"broadcast_sessions_with_response_duration_ms"; "broadcast_sessions_with_response_duration_ms";
private static final String KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS = private static final String KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
"note_response_event_for_all_broadcast_sessions"; "note_response_event_for_all_broadcast_sessions";
private static final String KEY_BROADCAST_RESPONSE_EXEMPTED_ROLES =
"brodacast_response_exempted_roles";
private static final String KEY_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS =
"brodacast_response_exempted_permissions";
public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS = public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
@@ -2837,6 +2882,11 @@ public class AppStandbyController
2 * ONE_MINUTE; 2 * ONE_MINUTE;
public static final boolean DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS = public static final boolean DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
true; true;
private static final String DEFAULT_BROADCAST_RESPONSE_EXEMPTED_ROLES = "";
private static final String DEFAULT_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS = "";
private final TextUtils.SimpleStringSplitter mStringPipeSplitter =
new TextUtils.SimpleStringSplitter('|');
ConstantsObserver(Handler handler) { ConstantsObserver(Handler handler) {
super(handler); super(handler);
@@ -2989,6 +3039,20 @@ public class AppStandbyController
KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS, KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS,
DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS); DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS);
break; break;
case KEY_BROADCAST_RESPONSE_EXEMPTED_ROLES:
mBroadcastResponseExemptedRoles = properties.getString(
KEY_BROADCAST_RESPONSE_EXEMPTED_ROLES,
DEFAULT_BROADCAST_RESPONSE_EXEMPTED_ROLES);
mBroadcastResponseExemptedRolesList = splitPipeSeparatedString(
mBroadcastResponseExemptedRoles);
break;
case KEY_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS:
mBroadcastResponseExemptedPermissions = properties.getString(
KEY_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS,
DEFAULT_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS);
mBroadcastResponseExemptedPermissionsList = splitPipeSeparatedString(
mBroadcastResponseExemptedPermissions);
break;
default: default:
if (!timeThresholdsUpdated if (!timeThresholdsUpdated
&& (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD) && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
@@ -3003,6 +3067,15 @@ public class AppStandbyController
} }
} }
private List<String> splitPipeSeparatedString(String string) {
final List<String> values = new ArrayList<>();
mStringPipeSplitter.setString(string);
while (mStringPipeSplitter.hasNext()) {
values.add(mStringPipeSplitter.next());
}
return values;
}
private void updateTimeThresholds() { private void updateTimeThresholds() {
// Query the values as an atomic set. // Query the values as an atomic set.
final DeviceConfig.Properties screenThresholdProperties = final DeviceConfig.Properties screenThresholdProperties =

View File

@@ -23,15 +23,23 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessState; import android.app.ActivityManager.ProcessState;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.BroadcastResponseStats; import android.app.usage.BroadcastResponseStats;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.permission.PermissionManager;
import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.LongArrayQueue; import android.util.LongArrayQueue;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -71,14 +79,33 @@ class BroadcastResponseStatsTracker {
private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats = private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats =
new SparseArray<>(); new SparseArray<>();
/**
* Cache of package names holding exempted roles.
*
* Contains the mapping of userId -> {roleName -> <packages>} data.
*/
// TODO: Use SparseArrayMap to simplify the logic.
@GuardedBy("mLock")
private SparseArray<ArrayMap<String, List<String>>> mExemptedRoleHoldersCache =
new SparseArray<>();
private final OnRoleHoldersChangedListener mRoleHoldersChangedListener =
this::onRoleHoldersChanged;
private AppStandbyInternal mAppStandby; private AppStandbyInternal mAppStandby;
private BroadcastResponseStatsLogger mLogger; private BroadcastResponseStatsLogger mLogger;
private RoleManager mRoleManager;
BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) { BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
mAppStandby = appStandby; mAppStandby = appStandby;
mLogger = new BroadcastResponseStatsLogger(); mLogger = new BroadcastResponseStatsLogger();
} }
void onSystemServicesReady(Context context) {
mRoleManager = context.getSystemService(RoleManager.class);
mRoleManager.addOnRoleHoldersChangedListenerAsUser(BackgroundThread.getExecutor(),
mRoleHoldersChangedListener, UserHandle.ALL);
}
// TODO (206518114): Move all callbacks handling to a handler thread. // TODO (206518114): Move all callbacks handling to a handler thread.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent, UserHandle targetUser, long idForResponseEvent,
@@ -86,10 +113,19 @@ class BroadcastResponseStatsTracker {
mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser, mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser,
idForResponseEvent, timestampMs, targetUidProcState); idForResponseEvent, timestampMs, targetUidProcState);
if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) { if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) {
// No need to track the broadcast response state while the target app is // No need to track the broadcast response stats while the target app is
// in the foreground. // in the foreground.
return; return;
} }
if (doesPackageHoldExemptedRole(targetPackage, targetUser)) {
// Package holds an exempted role, so no need to track the broadcast response stats.
return;
}
if (doesPackageHoldExemptedPermission(targetPackage, targetUser)) {
// Package holds an exempted permission, so no need to track the broadcast response
// stats
return;
}
synchronized (mLock) { synchronized (mLock) {
final ArraySet<BroadcastEvent> broadcastEvents = final ArraySet<BroadcastEvent> broadcastEvents =
getOrCreateBroadcastEventsLocked(targetPackage, targetUser); getOrCreateBroadcastEventsLocked(targetPackage, targetUser);
@@ -253,6 +289,62 @@ class BroadcastResponseStatsTracker {
} }
} }
boolean doesPackageHoldExemptedRole(@NonNull String packageName, @NonNull UserHandle user) {
final List<String> exemptedRoles = mAppStandby.getBroadcastResponseExemptedRoles();
synchronized (mLock) {
for (int i = exemptedRoles.size() - 1; i >= 0; --i) {
final String roleName = exemptedRoles.get(i);
final List<String> roleHolders = getRoleHoldersLocked(roleName, user);
if (CollectionUtils.contains(roleHolders, packageName)) {
return true;
}
}
}
return false;
}
boolean doesPackageHoldExemptedPermission(@NonNull String packageName,
@NonNull UserHandle user) {
final List<String> exemptedPermissions = mAppStandby
.getBroadcastResponseExemptedPermissions();
for (int i = exemptedPermissions.size() - 1; i >= 0; --i) {
final String permissionName = exemptedPermissions.get(i);
if (PermissionManager.checkPackageNamePermission(permissionName, packageName,
user.getIdentifier()) == PackageManager.PERMISSION_GRANTED) {
return true;
}
}
return false;
}
@GuardedBy("mLock")
@Nullable
private List<String> getRoleHoldersLocked(@NonNull String roleName, @NonNull UserHandle user) {
ArrayMap<String, List<String>> roleHoldersForUser = mExemptedRoleHoldersCache.get(
user.getIdentifier());
if (roleHoldersForUser == null) {
roleHoldersForUser = new ArrayMap<>();
mExemptedRoleHoldersCache.put(user.getIdentifier(), roleHoldersForUser);
}
List<String> roleHolders = roleHoldersForUser.get(roleName);
if (roleHolders == null && mRoleManager != null) {
roleHolders = mRoleManager.getRoleHoldersAsUser(roleName, user);
roleHoldersForUser.put(roleName, roleHolders);
}
return roleHolders;
}
private void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
synchronized (mLock) {
final ArrayMap<String, List<String>> roleHoldersForUser =
mExemptedRoleHoldersCache.get(user.getIdentifier());
if (roleHoldersForUser == null) {
return;
}
roleHoldersForUser.remove(roleName);
}
}
void onUserRemoved(@UserIdInt int userId) { void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) { synchronized (mLock) {
mUserBroadcastEvents.remove(userId); mUserBroadcastEvents.remove(userId);
@@ -260,6 +352,7 @@ class BroadcastResponseStatsTracker {
for (int i = mUserResponseStats.size() - 1; i >= 0; --i) { for (int i = mUserResponseStats.size() - 1; i >= 0; --i) {
mUserResponseStats.valueAt(i).remove(userId); mUserResponseStats.valueAt(i).remove(userId);
} }
mExemptedRoleHoldersCache.remove(userId);
} }
} }
@@ -373,6 +466,8 @@ class BroadcastResponseStatsTracker {
ipw.println(); ipw.println();
dumpResponseStatsLocked(ipw); dumpResponseStatsLocked(ipw);
ipw.println(); ipw.println();
dumpRoleHoldersLocked(ipw);
ipw.println();
mLogger.dumpLogs(ipw); mLogger.dumpLogs(ipw);
} }
@@ -417,5 +512,32 @@ class BroadcastResponseStatsTracker {
} }
ipw.decreaseIndent(); ipw.decreaseIndent();
} }
@GuardedBy("mLock")
private void dumpRoleHoldersLocked(@NonNull IndentingPrintWriter ipw) {
ipw.println("Role holders:");
ipw.increaseIndent();
for (int userIdx = 0; userIdx < mExemptedRoleHoldersCache.size(); ++userIdx) {
final int userId = mExemptedRoleHoldersCache.keyAt(userIdx);
final ArrayMap<String, List<String>> roleHoldersForUser =
mExemptedRoleHoldersCache.valueAt(userIdx);
ipw.println("User " + userId + ":");
ipw.increaseIndent();
for (int roleIdx = 0; roleIdx < roleHoldersForUser.size(); ++roleIdx) {
final String roleName = roleHoldersForUser.keyAt(roleIdx);
final List<String> holders = roleHoldersForUser.valueAt(roleIdx);
ipw.print(roleName + ": ");
for (int holderIdx = 0; holderIdx < holders.size(); ++holderIdx) {
if (holderIdx > 0) {
ipw.print(", ");
}
ipw.print(holders.get(holderIdx));
}
ipw.println();
}
ipw.decreaseIndent();
}
ipw.decreaseIndent();
}
} }

View File

@@ -357,6 +357,7 @@ public class UsageStatsService extends SystemService implements
getDpmInternal(); getDpmInternal();
// initialize mShortcutServiceInternal // initialize mShortcutServiceInternal
getShortcutServiceInternal(); getShortcutServiceInternal();
mResponseStatsTracker.onSystemServicesReady(getContext());
if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) { if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
try { try {