Merge "Track the number of PendingIntent per UID" into rvc-dev am: cf084d5a4a

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11955282

Change-Id: Ib2c9192fa66a25e6f3c1b347c4b756c8a0ceedf4
This commit is contained in:
Jing Ji
2020-06-24 05:37:35 +00:00
committed by Automerger Merge Worker
5 changed files with 117 additions and 5 deletions

View File

@@ -87,6 +87,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_PROCESS_START_ASYNC = "process_start_async";
static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -119,6 +120,7 @@ final class ActivityManagerConstants extends ContentObserver {
private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000;
// Flag stored in the DeviceConfig API.
/**
@@ -328,6 +330,12 @@ final class ActivityManagerConstants extends ContentObserver {
*/
public ArraySet<Integer> IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES = new ArraySet<Integer>();
/**
* The threshold for the amount of PendingIntent for each UID, there will be
* warning logs if the number goes beyond this threshold.
*/
public int PENDINGINTENT_WARNING_THRESHOLD = DEFAULT_PENDINGINTENT_WARNING_THRESHOLD;
private List<String> mDefaultImperceptibleKillExemptPackages;
private List<Integer> mDefaultImperceptibleKillExemptProcStates;
@@ -562,6 +570,8 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_MEMORY_INFO_THROTTLE_TIME);
TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
DEFAULT_TOP_TO_FGS_GRACE_DURATION);
PENDINGINTENT_WARNING_THRESHOLD = mParser.getInt(KEY_PENDINGINTENT_WARNING_THRESHOLD,
DEFAULT_PENDINGINTENT_WARNING_THRESHOLD);
// For new flags that are intended for server-side experiments, please use the new
// DeviceConfig package.

View File

@@ -2543,7 +2543,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mUiHandler = injector.getUiHandler(null /* service */);
mUserController = hasHandlerThread ? new UserController(this) : null;
mPendingIntentController = hasHandlerThread
? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
? new PendingIntentController(handlerThread.getLooper(), mUserController,
mConstants) : null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
@@ -2640,7 +2641,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mUserController = new UserController(this);
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController);
mHandlerThread.getLooper(), mUserController, mConstants);
if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
mUseFifoUiScheduling = true;

View File

@@ -41,8 +41,12 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.RingBuffer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
@@ -52,6 +56,7 @@ import com.android.server.wm.SafeActivityOptions;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
@@ -66,6 +71,9 @@ public class PendingIntentController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentController" : TAG_AM;
private static final String TAG_MU = TAG + POSTFIX_MU;
/** @see {@link #mRecentIntentsPerUid}. */
private static final int RECENT_N = 10;
/** Lock for internal state. */
final Object mLock = new Object();
final Handler mH;
@@ -77,10 +85,22 @@ public class PendingIntentController {
final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
= new HashMap<>();
PendingIntentController(Looper looper, UserController userController) {
/** The number of PendingIntentRecord per uid */
@GuardedBy("mLock")
private final SparseIntArray mIntentsPerUid = new SparseIntArray();
/** The recent PendingIntentRecord, up to {@link #RECENT_N} per uid */
@GuardedBy("mLock")
private final SparseArray<RingBuffer<String>> mRecentIntentsPerUid = new SparseArray<>();
private final ActivityManagerConstants mConstants;
PendingIntentController(Looper looper, UserController userController,
ActivityManagerConstants constants) {
mH = new Handler(looper);
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mUserController = userController;
mConstants = constants;
}
void onActivityManagerInternalAdded() {
@@ -136,12 +156,14 @@ public class PendingIntentController {
}
makeIntentSenderCanceled(rec);
mIntentSenderRecords.remove(key);
decrementUidStatLocked(rec);
}
if (noCreate) {
return rec;
}
rec = new PendingIntentRecord(this, key, callingUid);
mIntentSenderRecords.put(key, rec.ref);
incrementUidStatLocked(rec);
return rec;
}
}
@@ -198,6 +220,7 @@ public class PendingIntentController {
didSomething = true;
it.remove();
makeIntentSenderCanceled(pir);
decrementUidStatLocked(pir);
if (pir.key.activity != null) {
final Message m = PooledLambda.obtainMessage(
PendingIntentController::clearPendingResultForActivity, this,
@@ -237,6 +260,7 @@ public class PendingIntentController {
synchronized (mLock) {
makeIntentSenderCanceled(rec);
mIntentSenderRecords.remove(rec.key);
decrementUidStatLocked(rec);
if (cleanActivity && rec.key.activity != null) {
final Message m = PooledLambda.obtainMessage(
PendingIntentController::clearPendingResultForActivity, this,
@@ -369,9 +393,81 @@ public class PendingIntentController {
}
}
final int sizeOfIntentsPerUid = mIntentsPerUid.size();
if (sizeOfIntentsPerUid > 0) {
for (int i = 0; i < sizeOfIntentsPerUid; i++) {
pw.print(" * UID: ");
pw.print(mIntentsPerUid.keyAt(i));
pw.print(" total: ");
pw.println(mIntentsPerUid.valueAt(i));
}
}
if (!printed) {
pw.println(" (nothing)");
}
}
}
/**
* Increment the number of the PendingIntentRecord for the given uid, log a warning
* if there are too many for this uid already.
*/
@GuardedBy("mLock")
void incrementUidStatLocked(final PendingIntentRecord pir) {
final int uid = pir.uid;
final int idx = mIntentsPerUid.indexOfKey(uid);
int newCount = 1;
if (idx >= 0) {
newCount = mIntentsPerUid.valueAt(idx) + 1;
mIntentsPerUid.setValueAt(idx, newCount);
} else {
mIntentsPerUid.put(uid, newCount);
}
// If the number is within the range [threshold - N + 1, threshold], log it into buffer
final int lowBound = mConstants.PENDINGINTENT_WARNING_THRESHOLD - RECENT_N + 1;
RingBuffer<String> recentHistory = null;
if (newCount == lowBound) {
recentHistory = new RingBuffer(String.class, RECENT_N);
mRecentIntentsPerUid.put(uid, recentHistory);
} else if (newCount > lowBound && newCount <= mConstants.PENDINGINTENT_WARNING_THRESHOLD) {
recentHistory = mRecentIntentsPerUid.get(uid);
}
if (recentHistory == null) {
return;
}
recentHistory.append(pir.key.toString());
// Output the log if we are hitting the threshold
if (newCount == mConstants.PENDINGINTENT_WARNING_THRESHOLD) {
Slog.wtf(TAG, "Too many PendingIntent created for uid " + uid
+ ", recent " + RECENT_N + ": " + Arrays.toString(recentHistory.toArray()));
// Clear the buffer, as we don't want to spam the log when the numbers
// are jumping up and down around the threshold.
mRecentIntentsPerUid.remove(uid);
}
}
/**
* Decrement the number of the PendingIntentRecord for the given uid.
*/
@GuardedBy("mLock")
void decrementUidStatLocked(final PendingIntentRecord pir) {
final int uid = pir.uid;
final int idx = mIntentsPerUid.indexOfKey(uid);
if (idx >= 0) {
final int newCount = mIntentsPerUid.valueAt(idx) - 1;
// If we are going below the low threshold, no need to keep logs.
if (newCount == mConstants.PENDINGINTENT_WARNING_THRESHOLD - RECENT_N) {
mRecentIntentsPerUid.delete(uid);
}
if (newCount == 0) {
mIntentsPerUid.removeAt(idx);
} else {
mIntentsPerUid.setValueAt(idx, newCount);
}
}
}
}

View File

@@ -187,7 +187,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
+ " intent="
+ (requestIntent != null
? requestIntent.toShortString(false, true, false, false) : "<null>")
+ " flags=0x" + Integer.toHexString(flags) + " u=" + userId + "}";
+ " flags=0x" + Integer.toHexString(flags) + " u=" + userId + "}"
+ " requestCode=" + requestCode;
}
String typeName() {
@@ -499,6 +500,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
WeakReference<PendingIntentRecord> current = controller.mIntentSenderRecords.get(key);
if (current == ref) {
controller.mIntentSenderRecords.remove(key);
controller.decrementUidStatLocked(this);
}
}
}

View File

@@ -23,6 +23,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -81,8 +82,10 @@ public class PendingIntentControllerTest {
doReturn(mIPackageManager).when(() -> AppGlobals.getPackageManager());
when(mIPackageManager.getPackageUid(eq(TEST_PACKAGE_NAME), anyInt(), anyInt())).thenReturn(
TEST_CALLING_UID);
ActivityManagerConstants constants = mock(ActivityManagerConstants.class);
constants.PENDINGINTENT_WARNING_THRESHOLD = 2000;
mPendingIntentController = new PendingIntentController(Looper.getMainLooper(),
mUserController);
mUserController, constants);
mPendingIntentController.onActivityManagerInternalAdded();
}