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();
/**
* 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
* {@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.Settings.Global;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -420,6 +421,26 @@ public class AppStandbyController
volatile boolean mNoteResponseEventForAllBroadcastSessions =
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}.
*
@@ -1959,6 +1980,18 @@ public class AppStandbyController
return mNoteResponseEventForAllBroadcastSessions;
}
@Override
@NonNull
public List<String> getBroadcastResponseExemptedRoles() {
return mBroadcastResponseExemptedRolesList;
}
@Override
@NonNull
public List<String> getBroadcastResponseExemptedPermissions() {
return mBroadcastResponseExemptedPermissionsList;
}
@Override
@Nullable
public String getAppStandbyConstant(@NonNull String key) {
@@ -2311,6 +2344,14 @@ public class AppStandbyController
pw.print(mNoteResponseEventForAllBroadcastSessions);
pw.println();
pw.print(" mBroadcastResponseExemptedRoles");
pw.print(mBroadcastResponseExemptedRoles);
pw.println();
pw.print(" mBroadcastResponseExemptedPermissions");
pw.print(mBroadcastResponseExemptedPermissions);
pw.println();
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
pw.print(" mAllowRestrictedBucket=");
@@ -2795,6 +2836,10 @@ public class AppStandbyController
"broadcast_sessions_with_response_duration_ms";
private static final String KEY_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 =
COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
@@ -2837,6 +2882,11 @@ public class AppStandbyController
2 * ONE_MINUTE;
public static final boolean DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
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) {
super(handler);
@@ -2989,6 +3039,20 @@ public class AppStandbyController
KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS,
DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS);
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:
if (!timeThresholdsUpdated
&& (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() {
// Query the values as an atomic set.
final DeviceConfig.Properties screenThresholdProperties =

View File

@@ -23,15 +23,23 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessState;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.BroadcastResponseStats;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.SparseArray;
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 java.lang.annotation.Retention;
@@ -71,14 +79,33 @@ class BroadcastResponseStatsTracker {
private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats =
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 BroadcastResponseStatsLogger mLogger;
private RoleManager mRoleManager;
BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
mAppStandby = appStandby;
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.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent,
@@ -86,10 +113,19 @@ class BroadcastResponseStatsTracker {
mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser,
idForResponseEvent, timestampMs, targetUidProcState);
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.
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) {
final ArraySet<BroadcastEvent> broadcastEvents =
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) {
synchronized (mLock) {
mUserBroadcastEvents.remove(userId);
@@ -260,6 +352,7 @@ class BroadcastResponseStatsTracker {
for (int i = mUserResponseStats.size() - 1; i >= 0; --i) {
mUserResponseStats.valueAt(i).remove(userId);
}
mExemptedRoleHoldersCache.remove(userId);
}
}
@@ -373,6 +466,8 @@ class BroadcastResponseStatsTracker {
ipw.println();
dumpResponseStatsLocked(ipw);
ipw.println();
dumpRoleHoldersLocked(ipw);
ipw.println();
mLogger.dumpLogs(ipw);
}
@@ -417,5 +512,32 @@ class BroadcastResponseStatsTracker {
}
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();
// initialize mShortcutServiceInternal
getShortcutServiceInternal();
mResponseStatsTracker.onSystemServicesReady(getContext());
if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
try {