Merge "Check if the receiver can be skipped before starting a new process."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ad9f9dfa4a
@@ -731,6 +731,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maybeSkipReceiver(queue, null, r, index)) {
|
||||
mRunningColdStart = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
|
||||
final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
|
||||
|
||||
@@ -790,41 +795,53 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
* skipped (and therefore no more work is required).
|
||||
*/
|
||||
private boolean maybeSkipReceiver(@NonNull BroadcastProcessQueue queue,
|
||||
@NonNull BroadcastReceiverBatch batch, @NonNull BroadcastRecord r, int index) {
|
||||
@Nullable BroadcastReceiverBatch batch, @NonNull BroadcastRecord r, int index) {
|
||||
final String reason = shouldSkipReceiver(queue, r, index);
|
||||
if (reason != null) {
|
||||
if (batch == null) {
|
||||
enqueueFinishReceiver(queue, r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
|
||||
} else {
|
||||
batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, reason);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consults {@link BroadcastSkipPolicy} and the receiver process state to decide whether or
|
||||
* not the broadcast to a receiver can be skipped.
|
||||
*/
|
||||
private String shouldSkipReceiver(@NonNull BroadcastProcessQueue queue,
|
||||
@NonNull BroadcastRecord r, int index) {
|
||||
final int oldDeliveryState = getDeliveryState(r, index);
|
||||
final ProcessRecord app = queue.app;
|
||||
final Object receiver = r.receivers.get(index);
|
||||
|
||||
// If someone already finished this broadcast, finish immediately
|
||||
if (isDeliveryStateTerminal(oldDeliveryState)) {
|
||||
batch.finish(r, index, oldDeliveryState, "already terminal state");
|
||||
return true;
|
||||
return "already terminal state";
|
||||
}
|
||||
|
||||
// Consider additional cases where we'd want to finish immediately
|
||||
if (app.isInFullBackup()) {
|
||||
batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, "isInFullBackup");
|
||||
return true;
|
||||
if (app != null && app.isInFullBackup()) {
|
||||
return "isInFullBackup";
|
||||
}
|
||||
if (mSkipPolicy.shouldSkip(r, receiver)) {
|
||||
batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, "mSkipPolicy");
|
||||
return true;
|
||||
return "mSkipPolicy";
|
||||
}
|
||||
final Intent receiverIntent = r.getReceiverIntent(receiver);
|
||||
if (receiverIntent == null) {
|
||||
batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED, "getReceiverIntent");
|
||||
return true;
|
||||
return "getReceiverIntent";
|
||||
}
|
||||
|
||||
// Ignore registered receivers from a previous PID
|
||||
if ((receiver instanceof BroadcastFilter)
|
||||
&& ((BroadcastFilter) receiver).receiverList.pid != app.getPid()) {
|
||||
batch.finish(r, index, BroadcastRecord.DELIVERY_SKIPPED,
|
||||
"BroadcastFilter for mismatched PID");
|
||||
return true;
|
||||
return "BroadcastFilter for mismatched PID";
|
||||
}
|
||||
// The receiver was not handled in this method.
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,8 @@ import android.os.PowerExemptionManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
@@ -156,6 +158,7 @@ public class BroadcastQueueTest {
|
||||
private ActivityManagerService mAms;
|
||||
private BroadcastQueue mQueue;
|
||||
BroadcastConstants mConstants;
|
||||
private TestBroadcastSkipPolicy mSkipPolicy;
|
||||
|
||||
/**
|
||||
* Desired behavior of the next
|
||||
@@ -282,16 +285,8 @@ public class BroadcastQueueTest {
|
||||
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
|
||||
mConstants.TIMEOUT = 100;
|
||||
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
|
||||
final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
|
||||
public boolean shouldSkip(BroadcastRecord r, Object o) {
|
||||
// Ignored
|
||||
return false;
|
||||
}
|
||||
public String shouldSkipMessage(BroadcastRecord r, Object o) {
|
||||
// Ignored
|
||||
return null;
|
||||
}
|
||||
};
|
||||
mSkipPolicy = new TestBroadcastSkipPolicy(mAms);
|
||||
|
||||
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
|
||||
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
|
||||
// Ignored
|
||||
@@ -300,13 +295,13 @@ public class BroadcastQueueTest {
|
||||
|
||||
if (mImpl == Impl.DEFAULT) {
|
||||
var q = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
|
||||
mConstants, emptySkipPolicy, emptyHistory, false,
|
||||
mConstants, mSkipPolicy, emptyHistory, false,
|
||||
ProcessList.SCHED_GROUP_DEFAULT);
|
||||
q.mReceiverBatch.mDeepReceiverCopy = true;
|
||||
mQueue = q;
|
||||
} else if (mImpl == Impl.MODERN) {
|
||||
var q = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
|
||||
mConstants, mConstants, emptySkipPolicy, emptyHistory);
|
||||
mConstants, mConstants, mSkipPolicy, emptyHistory);
|
||||
q.mReceiverBatch.mDeepReceiverCopy = true;
|
||||
mQueue = q;
|
||||
} else {
|
||||
@@ -327,6 +322,43 @@ public class BroadcastQueueTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestBroadcastSkipPolicy extends BroadcastSkipPolicy {
|
||||
private final ArrayMap<String, ArraySet> mReceiversToSkip = new ArrayMap<>();
|
||||
|
||||
TestBroadcastSkipPolicy(ActivityManagerService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
public String shouldSkipMessage(BroadcastRecord r, Object o) {
|
||||
if (shouldSkipReceiver(r.intent.getAction(), o)) {
|
||||
return "test skipped receiver";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean shouldSkipReceiver(String action, Object o) {
|
||||
final ArraySet<Object> receiversToSkip = mReceiversToSkip.get(action);
|
||||
if (receiversToSkip == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < receiversToSkip.size(); ++i) {
|
||||
if (BroadcastRecord.isReceiverEquals(o, receiversToSkip.valueAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSkipReceiver(String action, Object o) {
|
||||
ArraySet<Object> receiversToSkip = mReceiversToSkip.get(action);
|
||||
if (receiversToSkip == null) {
|
||||
receiversToSkip = new ArraySet<>();
|
||||
mReceiversToSkip.put(action, receiversToSkip);
|
||||
}
|
||||
receiversToSkip.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestInjector extends Injector {
|
||||
TestInjector(Context context) {
|
||||
super(context);
|
||||
@@ -835,6 +867,7 @@ public class BroadcastQueueTest {
|
||||
static final String PACKAGE_GREEN = "com.example.green";
|
||||
static final String PACKAGE_BLUE = "com.example.blue";
|
||||
static final String PACKAGE_YELLOW = "com.example.yellow";
|
||||
static final String PACKAGE_ORANGE = "com.example.orange";
|
||||
|
||||
static final String PROCESS_SYSTEM = "system";
|
||||
|
||||
@@ -842,6 +875,7 @@ public class BroadcastQueueTest {
|
||||
static final String CLASS_GREEN = "com.example.green.Green";
|
||||
static final String CLASS_BLUE = "com.example.blue.Blue";
|
||||
static final String CLASS_YELLOW = "com.example.yellow.Yellow";
|
||||
static final String CLASS_ORANGE = "com.example.orange.Orange";
|
||||
|
||||
static int getUidForPackage(@NonNull String packageName) {
|
||||
switch (packageName) {
|
||||
@@ -851,6 +885,7 @@ public class BroadcastQueueTest {
|
||||
case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
|
||||
case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
|
||||
case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
|
||||
case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
|
||||
default: throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
@@ -1873,4 +1908,38 @@ public class BroadcastQueueTest {
|
||||
verify(mAms).addBroadcastStatLocked(eq(Intent.ACTION_TIMEZONE_CHANGED), eq(PACKAGE_RED),
|
||||
eq(1), eq(0), anyLong());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we skip broadcasts if {@link BroadcastSkipPolicy} decides it should be skipped.
|
||||
*/
|
||||
@Test
|
||||
public void testSkipPolicy() throws Exception {
|
||||
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
|
||||
final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
|
||||
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
|
||||
|
||||
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||
try (SyncBarrier b = new SyncBarrier()) {
|
||||
final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
|
||||
final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
|
||||
final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
|
||||
final Object orangeReceiver = makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE);
|
||||
enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
|
||||
List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));
|
||||
|
||||
mSkipPolicy.setSkipReceiver(airplane.getAction(), greenReceiver);
|
||||
mSkipPolicy.setSkipReceiver(airplane.getAction(), orangeReceiver);
|
||||
}
|
||||
|
||||
waitForIdle();
|
||||
// Verify that only blue and yellow receiver apps received the broadcast.
|
||||
verifyScheduleRegisteredReceiver(never(), receiverGreenApp, USER_SYSTEM);
|
||||
verifyScheduleRegisteredReceiver(receiverBlueApp, airplane);
|
||||
final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
|
||||
getUidForPackage(PACKAGE_YELLOW));
|
||||
verifyScheduleReceiver(receiverYellowApp, airplane);
|
||||
final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
|
||||
getUidForPackage(PACKAGE_ORANGE));
|
||||
assertNull(receiverOrangeApp);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user