Merge "Add APIs for querying broadcast response stats."
This commit is contained in:
committed by
Android (Google) Code Review
commit
077c836e99
@@ -2252,6 +2252,18 @@ package android.app.time {
|
||||
|
||||
package android.app.usage {
|
||||
|
||||
public final class BroadcastResponseStats implements android.os.Parcelable {
|
||||
ctor public BroadcastResponseStats(@NonNull String);
|
||||
method public int describeContents();
|
||||
method @IntRange(from=0) public int getBroadcastsDispatchedCount();
|
||||
method @IntRange(from=0) public int getNotificationsCancelledCount();
|
||||
method @IntRange(from=0) public int getNotificationsPostedCount();
|
||||
method @IntRange(from=0) public int getNotificationsUpdatedCount();
|
||||
method @NonNull public String getPackageName();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.BroadcastResponseStats> CREATOR;
|
||||
}
|
||||
|
||||
public final class CacheQuotaHint implements android.os.Parcelable {
|
||||
ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder);
|
||||
method public int describeContents();
|
||||
@@ -2307,11 +2319,13 @@ package android.app.usage {
|
||||
}
|
||||
|
||||
public final class UsageStatsManager {
|
||||
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
|
||||
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
|
||||
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
|
||||
method @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long getLastTimeAnyComponentUsed(@NonNull String);
|
||||
method public int getUsageSource();
|
||||
method @RequiresPermission(android.Manifest.permission.BIND_CARRIER_SERVICES) public void onCarrierPrivilegedAppsChanged();
|
||||
method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.BroadcastResponseStats queryBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
|
||||
method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
|
||||
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
|
||||
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
|
||||
|
||||
19
core/java/android/app/usage/BroadcastResponseStats.aidl
Normal file
19
core/java/android/app/usage/BroadcastResponseStats.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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 android.app.usage;
|
||||
|
||||
parcelable BroadcastResponseStats;
|
||||
186
core/java/android/app/usage/BroadcastResponseStats.java
Normal file
186
core/java/android/app/usage/BroadcastResponseStats.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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 android.app.usage;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Class containing a collection of stats related to response events started from an app
|
||||
* after receiving a broadcast.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class BroadcastResponseStats implements Parcelable {
|
||||
private final String mPackageName;
|
||||
private int mBroadcastsDispatchedCount;
|
||||
private int mNotificationsPostedCount;
|
||||
private int mNotificationsUpdatedCount;
|
||||
private int mNotificationsCancelledCount;
|
||||
|
||||
public BroadcastResponseStats(@NonNull String packageName) {
|
||||
mPackageName = packageName;
|
||||
}
|
||||
|
||||
private BroadcastResponseStats(@NonNull Parcel in) {
|
||||
mPackageName = in.readString8();
|
||||
mBroadcastsDispatchedCount = in.readInt();
|
||||
mNotificationsPostedCount = in.readInt();
|
||||
mNotificationsUpdatedCount = in.readInt();
|
||||
mNotificationsCancelledCount = in.readInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the package that the stats in this object correspond to.
|
||||
*/
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of broadcasts that were dispatched to the app by the caller.
|
||||
*
|
||||
* <b> Note that the returned count will only include the broadcasts that the caller explicitly
|
||||
* requested to record using
|
||||
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @return the total number of broadcasts that were dispatched to the app.
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
public int getBroadcastsDispatchedCount() {
|
||||
return mBroadcastsDispatchedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of notifications posted by the app soon after receiving a
|
||||
* broadcast.
|
||||
*
|
||||
* <b> Note that the returned count will only include the notifications that correspond to the
|
||||
* broadcasts that the caller explicitly requested to record using
|
||||
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @return the total number of notifications posted by the app soon after receiving
|
||||
* a broadcast.
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
public int getNotificationsPostedCount() {
|
||||
return mNotificationsPostedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of notifications updated by the app soon after receiving a
|
||||
* broadcast.
|
||||
*
|
||||
* <b> Note that the returned count will only include the notifications that correspond to the
|
||||
* broadcasts that the caller explicitly requested to record using
|
||||
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @return the total number of notifications updated by the app soon after receiving
|
||||
* a broadcast.
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
public int getNotificationsUpdatedCount() {
|
||||
return mNotificationsUpdatedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of notifications cancelled by the app soon after receiving a
|
||||
* broadcast.
|
||||
*
|
||||
* <b> Note that the returned count will only include the notifications that correspond to the
|
||||
* broadcasts that the caller explicitly requested to record using
|
||||
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @return the total number of notifications cancelled by the app soon after receiving
|
||||
* a broadcast.
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
public int getNotificationsCancelledCount() {
|
||||
return mNotificationsCancelledCount;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void incrementBroadcastsDispatchedCount(@IntRange(from = 0) int count) {
|
||||
mBroadcastsDispatchedCount += count;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void incrementNotificationsPostedCount(@IntRange(from = 0) int count) {
|
||||
mNotificationsPostedCount += count;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void incrementNotificationsUpdatedCount(@IntRange(from = 0) int count) {
|
||||
mNotificationsUpdatedCount += count;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void incrementNotificationsCancelledCount(@IntRange(from = 0) int count) {
|
||||
mNotificationsCancelledCount += count;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void addCounts(@NonNull BroadcastResponseStats stats) {
|
||||
incrementBroadcastsDispatchedCount(stats.getBroadcastsDispatchedCount());
|
||||
incrementNotificationsPostedCount(stats.getNotificationsPostedCount());
|
||||
incrementNotificationsUpdatedCount(stats.getNotificationsUpdatedCount());
|
||||
incrementNotificationsCancelledCount(stats.getNotificationsCancelledCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String toString() {
|
||||
return "stats {"
|
||||
+ "broadcastsSent=" + mBroadcastsDispatchedCount
|
||||
+ ",notificationsPosted=" + mNotificationsPostedCount
|
||||
+ ",notificationsUpdated=" + mNotificationsUpdatedCount
|
||||
+ ",notificationsCancelled=" + mNotificationsCancelledCount
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @ContentsFlags int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
|
||||
dest.writeString8(mPackageName);
|
||||
dest.writeInt(mBroadcastsDispatchedCount);
|
||||
dest.writeInt(mNotificationsPostedCount);
|
||||
dest.writeInt(mNotificationsUpdatedCount);
|
||||
dest.writeInt(mNotificationsCancelledCount);
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<BroadcastResponseStats> CREATOR =
|
||||
new Creator<BroadcastResponseStats>() {
|
||||
@Override
|
||||
public @NonNull BroadcastResponseStats createFromParcel(@NonNull Parcel source) {
|
||||
return new BroadcastResponseStats(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull BroadcastResponseStats[] newArray(int size) {
|
||||
return new BroadcastResponseStats[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.app.usage;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.usage.BroadcastResponseStats;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
|
||||
@@ -71,4 +72,10 @@ interface IUsageStatsManager {
|
||||
int getUsageSource();
|
||||
void forceUsageSourceSettingRead();
|
||||
long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
|
||||
BroadcastResponseStats queryBroadcastResponseStats(
|
||||
String packageName, long id, String callingPackage, int userId);
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
|
||||
void clearBroadcastResponseStats(String packageName, long id, String callingPackage,
|
||||
int userId);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.app.usage;
|
||||
|
||||
import android.annotation.CurrentTimeMillisLong;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
@@ -26,6 +27,7 @@ import android.annotation.SystemService;
|
||||
import android.annotation.TestApi;
|
||||
import android.annotation.UserHandleAware;
|
||||
import android.app.Activity;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.app.PendingIntent;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
@@ -1390,4 +1392,65 @@ public final class UsageStatsManager {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the broadcast response stats since the last boot corresponding to
|
||||
* {@code packageName} and {@code id}.
|
||||
*
|
||||
* <p>Broadcast response stats will include the aggregated data of what actions an app took upon
|
||||
* receiving a broadcast. This data will consider the broadcasts that the caller sent to
|
||||
* {@code packageName} and explicitly requested to record the response events using
|
||||
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @param packageName The name of the package that the caller wants to query for.
|
||||
* @param id The ID corresponding to the broadcasts that the caller wants to query for. This is
|
||||
* the ID the caller specifies when requesting a broadcast response event to be
|
||||
* recorded using {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @return the broadcast response stats corresponding to {@code packageName} and {@code id}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
|
||||
@UserHandleAware
|
||||
@NonNull
|
||||
public BroadcastResponseStats queryBroadcastResponseStats(
|
||||
@NonNull String packageName, @IntRange(from = 1) long id) {
|
||||
try {
|
||||
return mService.queryBroadcastResponseStats(packageName, id,
|
||||
mContext.getOpPackageName(), mContext.getUserId());
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the broadcast response stats corresponding to {@code packageName} and {@code id}.
|
||||
*
|
||||
* When a caller uses this API, stats related to the events occurring till that point will be
|
||||
* cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
|
||||
* return stats related to events occurring after this.
|
||||
*
|
||||
* @param packageName The name of the package that the caller wants to clear the data for.
|
||||
* @param id The ID corresponding to the broadcasts that the caller wants to clear the data for.
|
||||
* This is the ID the caller specifies when requesting a broadcast response event
|
||||
* to be recorded using
|
||||
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
|
||||
*
|
||||
* @see #queryBroadcastResponseStats(String, long)
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
|
||||
@UserHandleAware
|
||||
public void clearBroadcastResponseStats(@NonNull String packageName,
|
||||
@IntRange(from = 1) long id) {
|
||||
try {
|
||||
mService.clearBroadcastResponseStats(packageName, id,
|
||||
mContext.getOpPackageName(), mContext.getUserId());
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.usage;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Contains the data needed to identify a broadcast event.
|
||||
*/
|
||||
class BroadcastEvent {
|
||||
private int mSourceUid;
|
||||
private String mTargetPackage;
|
||||
private int mTargetUserId;
|
||||
private long mIdForResponseEvent;
|
||||
|
||||
BroadcastEvent(int sourceUid, @NonNull String targetPackage, @UserIdInt int targetUserId,
|
||||
long idForResponseEvent) {
|
||||
mSourceUid = sourceUid;
|
||||
mTargetPackage = targetPackage;
|
||||
mTargetUserId = targetUserId;
|
||||
mIdForResponseEvent = idForResponseEvent;
|
||||
}
|
||||
|
||||
public int getSourceUid() {
|
||||
return mSourceUid;
|
||||
}
|
||||
|
||||
public @NonNull String getTargetPackage() {
|
||||
return mTargetPackage;
|
||||
}
|
||||
|
||||
public @UserIdInt int getTargetUserId() {
|
||||
return mTargetUserId;
|
||||
}
|
||||
|
||||
public long getIdForResponseEvent() {
|
||||
return mIdForResponseEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !(obj instanceof BroadcastEvent)) {
|
||||
return false;
|
||||
}
|
||||
final BroadcastEvent other = (BroadcastEvent) obj;
|
||||
return this.mSourceUid == other.mSourceUid
|
||||
&& this.mIdForResponseEvent == other.mIdForResponseEvent
|
||||
&& this.mTargetUserId == other.mTargetUserId
|
||||
&& this.mTargetPackage.equals(other.mTargetPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mSourceUid, mTargetPackage, mTargetUserId,
|
||||
mIdForResponseEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String toString() {
|
||||
return "BroadcastEvent {"
|
||||
+ "srcUid=" + mSourceUid
|
||||
+ ",tgtPkg=" + mTargetPackage
|
||||
+ ",tgtUser=" + mTargetUserId
|
||||
+ ",id=" + mIdForResponseEvent
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 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.usage;
|
||||
|
||||
import android.annotation.ElapsedRealtimeLong;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.usage.BroadcastResponseStats;
|
||||
import android.os.UserHandle;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
class BroadcastResponseStatsTracker {
|
||||
private static final String TAG = "ResponseStatsTracker";
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = {"NOTIFICATION_EVENT"}, value = {
|
||||
NOTIFICATION_EVENT_POSTED,
|
||||
NOTIFICATION_EVENT_UPDATED,
|
||||
NOTIFICATION_EVENT_CANCELLED
|
||||
})
|
||||
public @interface NotificationEvent {}
|
||||
|
||||
private static final int NOTIFICATION_EVENT_POSTED = 0;
|
||||
private static final int NOTIFICATION_EVENT_UPDATED = 1;
|
||||
private static final int NOTIFICATION_EVENT_CANCELLED = 2;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
/**
|
||||
* Contains the mapping of user -> UserBroadcastEvents data.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private SparseArray<UserBroadcastEvents> mUserBroadcastEvents = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Contains the mapping of sourceUid -> {targetUser -> UserBroadcastResponseStats} data.
|
||||
* Here sourceUid refers to the uid that sent a broadcast and targetUser is the user that the
|
||||
* broadcast was directed to.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats =
|
||||
new SparseArray<>();
|
||||
|
||||
// TODO (206518114): Move all callbacks handling to a handler thread.
|
||||
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
|
||||
UserHandle targetUser, long idForResponseEvent,
|
||||
@ElapsedRealtimeLong long timestampMs) {
|
||||
synchronized (mLock) {
|
||||
final LongSparseArray<BroadcastEvent> broadcastEvents =
|
||||
getOrCreateBroadcastEventsLocked(targetPackage, targetUser);
|
||||
final BroadcastEvent broadcastEvent = new BroadcastEvent(
|
||||
sourceUid, targetPackage, targetUser.getIdentifier(), idForResponseEvent);
|
||||
broadcastEvents.append(timestampMs, broadcastEvent);
|
||||
final BroadcastResponseStats responseStats =
|
||||
getOrCreateBroadcastResponseStats(broadcastEvent);
|
||||
responseStats.incrementBroadcastsDispatchedCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
void reportNotificationPosted(@NonNull String packageName, UserHandle user,
|
||||
@ElapsedRealtimeLong long timestampMs) {
|
||||
reportNotificationEvent(NOTIFICATION_EVENT_POSTED, packageName, user, timestampMs);
|
||||
}
|
||||
|
||||
void reportNotificationUpdated(@NonNull String packageName, UserHandle user,
|
||||
@ElapsedRealtimeLong long timestampMs) {
|
||||
reportNotificationEvent(NOTIFICATION_EVENT_UPDATED, packageName, user, timestampMs);
|
||||
|
||||
}
|
||||
|
||||
void reportNotificationCancelled(@NonNull String packageName, UserHandle user,
|
||||
@ElapsedRealtimeLong long timestampMs) {
|
||||
reportNotificationEvent(NOTIFICATION_EVENT_CANCELLED, packageName, user, timestampMs);
|
||||
}
|
||||
|
||||
private void reportNotificationEvent(@NotificationEvent int event,
|
||||
@NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
|
||||
// TODO (206518114): Store last N events to dump for debugging purposes.
|
||||
synchronized (mLock) {
|
||||
final LongSparseArray<BroadcastEvent> broadcastEvents =
|
||||
getBroadcastEventsLocked(packageName, user);
|
||||
if (broadcastEvents == null) {
|
||||
return;
|
||||
}
|
||||
// TODO (206518114): Add LongSparseArray.removeAtRange()
|
||||
for (int i = broadcastEvents.size() - 1; i >= 0; --i) {
|
||||
final long dispatchTimestampMs = broadcastEvents.keyAt(i);
|
||||
final long elapsedDurationMs = timestampMs - dispatchTimestampMs;
|
||||
if (elapsedDurationMs <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (dispatchTimestampMs >= timestampMs) {
|
||||
continue;
|
||||
}
|
||||
// TODO (206518114): Make the constant configurable.
|
||||
if (elapsedDurationMs <= 2 * 60 * 1000) {
|
||||
final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i);
|
||||
final BroadcastResponseStats responseStats =
|
||||
getBroadcastResponseStats(broadcastEvent);
|
||||
if (responseStats == null) {
|
||||
continue;
|
||||
}
|
||||
switch (event) {
|
||||
case NOTIFICATION_EVENT_POSTED:
|
||||
responseStats.incrementNotificationsPostedCount(1);
|
||||
break;
|
||||
case NOTIFICATION_EVENT_UPDATED:
|
||||
responseStats.incrementNotificationsUpdatedCount(1);
|
||||
break;
|
||||
case NOTIFICATION_EVENT_CANCELLED:
|
||||
responseStats.incrementNotificationsCancelledCount(1);
|
||||
break;
|
||||
default:
|
||||
Slog.wtf(TAG, "Unknown event: " + event);
|
||||
}
|
||||
}
|
||||
broadcastEvents.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull BroadcastResponseStats queryBroadcastResponseStats(int callingUid,
|
||||
@NonNull String packageName, long id, @UserIdInt int userId) {
|
||||
final BroadcastResponseStats aggregatedResponseStats =
|
||||
new BroadcastResponseStats(packageName);
|
||||
synchronized (mLock) {
|
||||
final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
|
||||
mUserResponseStats.get(callingUid);
|
||||
if (responseStatsForCaller == null) {
|
||||
return aggregatedResponseStats;
|
||||
}
|
||||
final UserBroadcastResponseStats responseStatsForUser =
|
||||
responseStatsForCaller.get(userId);
|
||||
if (responseStatsForUser == null) {
|
||||
return aggregatedResponseStats;
|
||||
}
|
||||
responseStatsForUser.aggregateBroadcastResponseStats(aggregatedResponseStats,
|
||||
packageName, id);
|
||||
}
|
||||
return aggregatedResponseStats;
|
||||
}
|
||||
|
||||
void clearBroadcastResponseStats(int callingUid, @NonNull String packageName, long id,
|
||||
@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
|
||||
mUserResponseStats.get(callingUid);
|
||||
if (responseStatsForCaller == null) {
|
||||
return;
|
||||
}
|
||||
final UserBroadcastResponseStats responseStatsForUser =
|
||||
responseStatsForCaller.get(userId);
|
||||
if (responseStatsForUser == null) {
|
||||
return;
|
||||
}
|
||||
responseStatsForUser.clearBroadcastResponseStats(packageName, id);
|
||||
}
|
||||
}
|
||||
|
||||
void onUserRemoved(@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
mUserBroadcastEvents.remove(userId);
|
||||
|
||||
for (int i = mUserResponseStats.size() - 1; i >= 0; --i) {
|
||||
mUserResponseStats.valueAt(i).remove(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onPackageRemoved(@NonNull String packageName, @UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(userId);
|
||||
if (userBroadcastEvents != null) {
|
||||
userBroadcastEvents.onPackageRemoved(packageName);
|
||||
}
|
||||
|
||||
for (int i = mUserResponseStats.size() - 1; i >= 0; --i) {
|
||||
final UserBroadcastResponseStats userResponseStats =
|
||||
mUserResponseStats.valueAt(i).get(userId);
|
||||
if (userResponseStats != null) {
|
||||
userResponseStats.onPackageRemoved(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onUidRemoved(int uid) {
|
||||
synchronized (mLock) {
|
||||
for (int i = mUserBroadcastEvents.size() - 1; i >= 0; --i) {
|
||||
mUserBroadcastEvents.valueAt(i).onUidRemoved(uid);
|
||||
}
|
||||
|
||||
mUserResponseStats.remove(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
private LongSparseArray<BroadcastEvent> getBroadcastEventsLocked(
|
||||
@NonNull String packageName, UserHandle user) {
|
||||
final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(
|
||||
user.getIdentifier());
|
||||
if (userBroadcastEvents == null) {
|
||||
return null;
|
||||
}
|
||||
return userBroadcastEvents.getBroadcastEvents(packageName);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private LongSparseArray<BroadcastEvent> getOrCreateBroadcastEventsLocked(
|
||||
@NonNull String packageName, UserHandle user) {
|
||||
UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(user.getIdentifier());
|
||||
if (userBroadcastEvents == null) {
|
||||
userBroadcastEvents = new UserBroadcastEvents();
|
||||
mUserBroadcastEvents.put(user.getIdentifier(), userBroadcastEvents);
|
||||
}
|
||||
return userBroadcastEvents.getOrCreateBroadcastEvents(packageName);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
private BroadcastResponseStats getBroadcastResponseStats(
|
||||
@NonNull BroadcastEvent broadcastEvent) {
|
||||
final int sourceUid = broadcastEvent.getSourceUid();
|
||||
final SparseArray<UserBroadcastResponseStats> responseStatsForUid =
|
||||
mUserResponseStats.get(sourceUid);
|
||||
return getBroadcastResponseStats(responseStatsForUid, broadcastEvent);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
private BroadcastResponseStats getBroadcastResponseStats(
|
||||
@Nullable SparseArray<UserBroadcastResponseStats> responseStatsForUid,
|
||||
@NonNull BroadcastEvent broadcastEvent) {
|
||||
if (responseStatsForUid == null) {
|
||||
return null;
|
||||
}
|
||||
final UserBroadcastResponseStats userResponseStats = responseStatsForUid.get(
|
||||
broadcastEvent.getTargetUserId());
|
||||
if (userResponseStats == null) {
|
||||
return null;
|
||||
}
|
||||
return userResponseStats.getBroadcastResponseStats(broadcastEvent);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private BroadcastResponseStats getOrCreateBroadcastResponseStats(
|
||||
@NonNull BroadcastEvent broadcastEvent) {
|
||||
final int sourceUid = broadcastEvent.getSourceUid();
|
||||
SparseArray<UserBroadcastResponseStats> userResponseStatsForUid =
|
||||
mUserResponseStats.get(sourceUid);
|
||||
if (userResponseStatsForUid == null) {
|
||||
userResponseStatsForUid = new SparseArray<>();
|
||||
mUserResponseStats.put(sourceUid, userResponseStatsForUid);
|
||||
}
|
||||
UserBroadcastResponseStats userResponseStats = userResponseStatsForUid.get(
|
||||
broadcastEvent.getTargetUserId());
|
||||
if (userResponseStats == null) {
|
||||
userResponseStats = new UserBroadcastResponseStats();
|
||||
userResponseStatsForUid.put(broadcastEvent.getTargetUserId(), userResponseStats);
|
||||
}
|
||||
return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
|
||||
}
|
||||
|
||||
void dump(@NonNull IndentingPrintWriter ipw) {
|
||||
ipw.println("Broadcast response stats:");
|
||||
ipw.increaseIndent();
|
||||
|
||||
synchronized (mLock) {
|
||||
dumpBroadcastEventsLocked(ipw);
|
||||
ipw.println();
|
||||
dumpResponseStatsLocked(ipw);
|
||||
}
|
||||
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void dumpBroadcastEventsLocked(@NonNull IndentingPrintWriter ipw) {
|
||||
ipw.println("Broadcast events:");
|
||||
ipw.increaseIndent();
|
||||
for (int i = 0; i < mUserBroadcastEvents.size(); ++i) {
|
||||
final int userId = mUserBroadcastEvents.keyAt(i);
|
||||
final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.valueAt(i);
|
||||
ipw.println("User " + userId + ":");
|
||||
ipw.increaseIndent();
|
||||
userBroadcastEvents.dump(ipw);
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void dumpResponseStatsLocked(@NonNull IndentingPrintWriter ipw) {
|
||||
ipw.println("Response stats:");
|
||||
ipw.increaseIndent();
|
||||
for (int i = 0; i < mUserResponseStats.size(); ++i) {
|
||||
final int sourceUid = mUserResponseStats.keyAt(i);
|
||||
final SparseArray<UserBroadcastResponseStats> userBroadcastResponseStats =
|
||||
mUserResponseStats.valueAt(i);
|
||||
ipw.println("Uid " + sourceUid + ":");
|
||||
ipw.increaseIndent();
|
||||
for (int j = 0; j < userBroadcastResponseStats.size(); ++j) {
|
||||
final int userId = userBroadcastResponseStats.keyAt(j);
|
||||
final UserBroadcastResponseStats broadcastResponseStats =
|
||||
userBroadcastResponseStats.valueAt(j);
|
||||
ipw.println("User " + userId + ":");
|
||||
ipw.increaseIndent();
|
||||
broadcastResponseStats.dump(ipw);
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,17 @@ import static android.app.usage.UsageEvents.Event.USER_STOPPED;
|
||||
import static android.app.usage.UsageEvents.Event.USER_UNLOCKED;
|
||||
import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
|
||||
import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;
|
||||
import static android.content.Intent.ACTION_UID_REMOVED;
|
||||
import static android.content.Intent.EXTRA_UID;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.CurrentTimeMillisLong;
|
||||
import android.annotation.ElapsedRealtimeLong;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
@@ -44,6 +48,7 @@ import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.app.usage.AppLaunchEstimateInfo;
|
||||
import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.BroadcastResponseStats;
|
||||
import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.EventStats;
|
||||
import android.app.usage.IUsageStatsManager;
|
||||
@@ -217,6 +222,8 @@ public class UsageStatsService extends SystemService implements
|
||||
private final CopyOnWriteArraySet<UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener>
|
||||
mEstimatedLaunchTimeChangedListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
private BroadcastResponseStatsTracker mResponseStatsTracker;
|
||||
|
||||
private static class ActivityData {
|
||||
private final String mTaskRootPackage;
|
||||
private final String mTaskRootClass;
|
||||
@@ -263,6 +270,7 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("AndroidFrameworkRequiresPermission")
|
||||
public void onStart() {
|
||||
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
|
||||
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
|
||||
@@ -271,6 +279,7 @@ public class UsageStatsService extends SystemService implements
|
||||
mHandler = new H(BackgroundThread.get().getLooper());
|
||||
|
||||
mAppStandby = mInjector.getAppStandbyController(getContext());
|
||||
mResponseStatsTracker = new BroadcastResponseStatsTracker();
|
||||
|
||||
mAppTimeLimit = new AppTimeLimitController(getContext(),
|
||||
new AppTimeLimitController.TimeLimitCallbackListener() {
|
||||
@@ -315,6 +324,9 @@ public class UsageStatsService extends SystemService implements
|
||||
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
|
||||
null, mHandler);
|
||||
|
||||
getContext().registerReceiverAsUser(new UidRemovedReceiver(), UserHandle.ALL,
|
||||
new IntentFilter(ACTION_UID_REMOVED), null, mHandler);
|
||||
|
||||
mRealTimeSnapshot = SystemClock.elapsedRealtime();
|
||||
mSystemTimeSnapshot = System.currentTimeMillis();
|
||||
|
||||
@@ -536,6 +548,7 @@ public class UsageStatsService extends SystemService implements
|
||||
if (Intent.ACTION_USER_REMOVED.equals(action)) {
|
||||
if (userId >= 0) {
|
||||
mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
|
||||
mResponseStatsTracker.onUserRemoved(userId);
|
||||
}
|
||||
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
|
||||
if (userId >= 0) {
|
||||
@@ -545,6 +558,20 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
private class UidRemovedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final int uid = intent.getIntExtra(EXTRA_UID, -1);
|
||||
if (uid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
mResponseStatsTracker.onUidRemoved(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
|
||||
@Override
|
||||
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
|
||||
@@ -1780,6 +1807,10 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if ("broadcast-response-stats".equals(arg)) {
|
||||
synchronized (mLock) {
|
||||
mResponseStatsTracker.dump(idpw);
|
||||
}
|
||||
} else if (arg != null && !arg.startsWith("-")) {
|
||||
// Anything else that doesn't start with '-' is a pkg to filter
|
||||
pkgs.add(arg);
|
||||
@@ -1813,6 +1844,9 @@ public class UsageStatsService extends SystemService implements
|
||||
idpw.println();
|
||||
|
||||
mAppTimeLimit.dump(null, pw);
|
||||
|
||||
idpw.println();
|
||||
mResponseStatsTracker.dump(idpw);
|
||||
}
|
||||
|
||||
mAppStandby.dumpUsers(idpw, userIds, pkgs);
|
||||
@@ -2645,6 +2679,60 @@ public class UsageStatsService extends SystemService implements
|
||||
/ TimeUnit.DAYS.toMillis(1) * TimeUnit.DAYS.toMillis(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public BroadcastResponseStats queryBroadcastResponseStats(
|
||||
@NonNull String packageName,
|
||||
@IntRange(from = 1) long id,
|
||||
@NonNull String callingPackage,
|
||||
@UserIdInt int userId) {
|
||||
Objects.requireNonNull(packageName);
|
||||
Objects.requireNonNull(callingPackage);
|
||||
// TODO: Move to Preconditions utility class
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException("id needs to be >0");
|
||||
}
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
if (!hasPermission(callingPackage)) {
|
||||
throw new SecurityException(
|
||||
"Caller does not have the permission needed to call this API; "
|
||||
+ "callingPackage=" + callingPackage
|
||||
+ ", callingUid=" + callingUid);
|
||||
}
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
|
||||
userId, false /* allowAll */, false /* requireFull */,
|
||||
"queryBroadcastResponseStats" /* name */, callingPackage);
|
||||
return mResponseStatsTracker.queryBroadcastResponseStats(
|
||||
callingUid, packageName, id, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBroadcastResponseStats(
|
||||
@NonNull String packageName,
|
||||
@IntRange(from = 1) long id,
|
||||
@NonNull String callingPackage,
|
||||
@UserIdInt int userId) {
|
||||
Objects.requireNonNull(packageName);
|
||||
Objects.requireNonNull(callingPackage);
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException("id needs to be >0");
|
||||
}
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
if (!hasPermission(callingPackage)) {
|
||||
throw new SecurityException(
|
||||
"Caller does not have the permission needed to call this API; "
|
||||
+ "callingPackage=" + callingPackage
|
||||
+ ", callingUid=" + callingUid);
|
||||
}
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
|
||||
userId, false /* allowAll */, false /* requireFull */,
|
||||
"clearBroadcastResponseStats" /* name */, callingPackage);
|
||||
mResponseStatsTracker.clearBroadcastResponseStats(callingUid,
|
||||
packageName, id, userId);
|
||||
}
|
||||
}
|
||||
|
||||
void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
|
||||
@@ -2953,21 +3041,26 @@ public class UsageStatsService extends SystemService implements
|
||||
public void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
|
||||
@NonNull UserHandle targetUser, long idForResponseEvent,
|
||||
@ElapsedRealtimeLong long timestampMs) {
|
||||
mResponseStatsTracker.reportBroadcastDispatchEvent(sourceUid, targetPackage,
|
||||
targetUser, idForResponseEvent, timestampMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportNotificationPosted(@NonNull String packageName,
|
||||
@NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs) {
|
||||
mResponseStatsTracker.reportNotificationPosted(packageName, user, timestampMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportNotificationUpdated(@NonNull String packageName,
|
||||
@NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs) {
|
||||
mResponseStatsTracker.reportNotificationUpdated(packageName, user, timestampMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportNotificationRemoved(@NonNull String packageName,
|
||||
@NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs) {
|
||||
mResponseStatsTracker.reportNotificationCancelled(packageName, user, timestampMs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2980,6 +3073,7 @@ public class UsageStatsService extends SystemService implements
|
||||
mHandler.obtainMessage(MSG_PACKAGE_REMOVED, changingUserId, 0, packageName)
|
||||
.sendToTarget();
|
||||
}
|
||||
mResponseStatsTracker.onPackageRemoved(packageName, UserHandle.getUserId(uid));
|
||||
super.onPackageRemoved(packageName, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.usage;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
class UserBroadcastEvents {
|
||||
/**
|
||||
* Contains the mapping of targetPackage -> {BroadcastEvent} data.
|
||||
* Here targetPackage refers to the package receiving the broadcast and BroadcastEvent objects
|
||||
* corresponding to each broadcast it is receiving.
|
||||
*/
|
||||
private ArrayMap<String, LongSparseArray<BroadcastEvent>> mBroadcastEvents = new ArrayMap();
|
||||
|
||||
@Nullable LongSparseArray<BroadcastEvent> getBroadcastEvents(@NonNull String packageName) {
|
||||
return mBroadcastEvents.get(packageName);
|
||||
}
|
||||
|
||||
@NonNull LongSparseArray<BroadcastEvent> getOrCreateBroadcastEvents(
|
||||
@NonNull String packageName) {
|
||||
LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.get(packageName);
|
||||
if (broadcastEvents == null) {
|
||||
broadcastEvents = new LongSparseArray<>();
|
||||
mBroadcastEvents.put(packageName, broadcastEvents);
|
||||
}
|
||||
return broadcastEvents;
|
||||
}
|
||||
|
||||
void onPackageRemoved(@NonNull String packageName) {
|
||||
mBroadcastEvents.remove(packageName);
|
||||
}
|
||||
|
||||
void onUidRemoved(int uid) {
|
||||
for (int i = mBroadcastEvents.size() - 1; i >= 0; --i) {
|
||||
final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
|
||||
for (int j = broadcastEvents.size() - 1; j >= 0; --j) {
|
||||
if (broadcastEvents.valueAt(j).getSourceUid() == uid) {
|
||||
broadcastEvents.removeAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump(@NonNull IndentingPrintWriter ipw) {
|
||||
for (int i = 0; i < mBroadcastEvents.size(); ++i) {
|
||||
final String packageName = mBroadcastEvents.keyAt(i);
|
||||
final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
|
||||
ipw.println(packageName + ":");
|
||||
ipw.increaseIndent();
|
||||
if (broadcastEvents.size() == 0) {
|
||||
ipw.println("<empty>");
|
||||
} else {
|
||||
for (int j = 0; j < broadcastEvents.size(); ++j) {
|
||||
final long timestampMs = broadcastEvents.keyAt(j);
|
||||
final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(j);
|
||||
TimeUtils.formatDuration(timestampMs, ipw);
|
||||
ipw.print(": ");
|
||||
ipw.println(broadcastEvent);
|
||||
}
|
||||
}
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.usage;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.usage.BroadcastResponseStats;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
class UserBroadcastResponseStats {
|
||||
/**
|
||||
* Contains the mapping of a BroadcastEvent type to it's aggregated stats.
|
||||
*/
|
||||
private ArrayMap<BroadcastEvent, BroadcastResponseStats> mResponseStats =
|
||||
new ArrayMap<>();
|
||||
|
||||
@Nullable BroadcastResponseStats getBroadcastResponseStats(
|
||||
BroadcastEvent broadcastEvent) {
|
||||
return mResponseStats.get(broadcastEvent);
|
||||
}
|
||||
|
||||
@NonNull BroadcastResponseStats getOrCreateBroadcastResponseStats(
|
||||
BroadcastEvent broadcastEvent) {
|
||||
BroadcastResponseStats responseStats = mResponseStats.get(broadcastEvent);
|
||||
if (responseStats == null) {
|
||||
responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage());
|
||||
mResponseStats.put(broadcastEvent, responseStats);
|
||||
}
|
||||
return responseStats;
|
||||
}
|
||||
|
||||
void aggregateBroadcastResponseStats(
|
||||
@NonNull BroadcastResponseStats responseStats,
|
||||
@NonNull String packageName, long id) {
|
||||
for (int i = mResponseStats.size() - 1; i >= 0; --i) {
|
||||
final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
|
||||
if (broadcastEvent.getIdForResponseEvent() == id
|
||||
&& broadcastEvent.getTargetPackage().equals(packageName)) {
|
||||
responseStats.addCounts(mResponseStats.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearBroadcastResponseStats(@NonNull String packageName, long id) {
|
||||
for (int i = mResponseStats.size() - 1; i >= 0; --i) {
|
||||
final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
|
||||
if (broadcastEvent.getIdForResponseEvent() == id
|
||||
&& broadcastEvent.getTargetPackage().equals(packageName)) {
|
||||
mResponseStats.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onPackageRemoved(@NonNull String packageName) {
|
||||
for (int i = mResponseStats.size() - 1; i >= 0; --i) {
|
||||
if (mResponseStats.keyAt(i).getTargetPackage().equals(packageName)) {
|
||||
mResponseStats.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump(@NonNull IndentingPrintWriter ipw) {
|
||||
for (int i = 0; i < mResponseStats.size(); ++i) {
|
||||
final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
|
||||
final BroadcastResponseStats responseStats = mResponseStats.valueAt(i);
|
||||
ipw.print(broadcastEvent);
|
||||
ipw.print(" -> ");
|
||||
ipw.println(responseStats);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user