Add tests for duplicate PendingIntent requests
Add tests for requestNetwork and registerNetworkCallback with PendingIntents that are equal as per PendingIntent.intentFilterEquals. In such situations, the first request is documented to be automatically unregistered. This means that the PendingIntent should only be fired once, instead of being fired for both requests. Currently this behavior is broken for network callbacks (not for network requests). Test: atest ConnectivityManagerTest Bug: 174237997 Original-Change: https://android-review.googlesource.com/1742073 Merged-In: Ic636c731c71474ef09043d7a26ccfd6e2bb5c39f Change-Id: Ic636c731c71474ef09043d7a26ccfd6e2bb5c39f
This commit is contained in:
@@ -29,6 +29,8 @@ import static android.content.pm.PackageManager.FEATURE_WIFI;
|
||||
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
|
||||
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.net.ConnectivityManager.EXTRA_NETWORK;
|
||||
import static android.net.ConnectivityManager.EXTRA_NETWORK_REQUEST;
|
||||
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
|
||||
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
|
||||
import static android.net.ConnectivityManager.TYPE_ETHERNET;
|
||||
@@ -74,12 +76,14 @@ import static android.system.OsConstants.AF_UNSPEC;
|
||||
import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
|
||||
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
|
||||
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
|
||||
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
|
||||
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
|
||||
import static com.android.testutils.MiscAsserts.assertThrows;
|
||||
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
|
||||
import static com.android.testutils.TestPermissionUtil.runAsShell;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
@@ -206,6 +210,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -849,6 +854,119 @@ public class ConnectivityManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void runIdenticalPendingIntentsRequestTest(boolean useListen) throws Exception {
|
||||
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
|
||||
|
||||
// Disconnect before registering callbacks, reconnect later to fire them
|
||||
mCtsNetUtils.ensureWifiDisconnected(null);
|
||||
|
||||
final NetworkRequest firstRequest = makeWifiNetworkRequest();
|
||||
final NetworkRequest secondRequest = new NetworkRequest(firstRequest);
|
||||
// Will match wifi or test, since transports are ORed; but there should only be wifi
|
||||
secondRequest.networkCapabilities.addTransportType(TRANSPORT_TEST);
|
||||
|
||||
PendingIntent firstIntent = null;
|
||||
PendingIntent secondIntent = null;
|
||||
BroadcastReceiver receiver = null;
|
||||
|
||||
// Avoid receiving broadcasts from other runs by appending a timestamp
|
||||
final String broadcastAction = NETWORK_CALLBACK_ACTION + System.currentTimeMillis();
|
||||
try {
|
||||
// TODO: replace with PendingIntent.FLAG_MUTABLE when this code compiles against S+
|
||||
// Intent is mutable to receive EXTRA_NETWORK_REQUEST from ConnectivityService
|
||||
final int pendingIntentFlagMutable = 1 << 25;
|
||||
final String extraBoolKey = "extra_bool";
|
||||
firstIntent = PendingIntent.getBroadcast(mContext,
|
||||
0 /* requestCode */,
|
||||
new Intent(broadcastAction).putExtra(extraBoolKey, false),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
|
||||
|
||||
if (useListen) {
|
||||
mCm.registerNetworkCallback(firstRequest, firstIntent);
|
||||
} else {
|
||||
mCm.requestNetwork(firstRequest, firstIntent);
|
||||
}
|
||||
|
||||
// Second intent equals the first as per filterEquals (extras don't count), so first
|
||||
// intent will be updated with the new extras
|
||||
secondIntent = PendingIntent.getBroadcast(mContext,
|
||||
0 /* requestCode */,
|
||||
new Intent(broadcastAction).putExtra(extraBoolKey, true),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
|
||||
|
||||
// Because secondIntent.intentFilterEquals the first, the request should be replaced
|
||||
if (useListen) {
|
||||
mCm.registerNetworkCallback(secondRequest, secondIntent);
|
||||
} else {
|
||||
mCm.requestNetwork(secondRequest, secondIntent);
|
||||
}
|
||||
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(broadcastAction);
|
||||
|
||||
final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
|
||||
final AtomicInteger receivedCount = new AtomicInteger(0);
|
||||
receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final NetworkRequest request = intent.getParcelableExtra(EXTRA_NETWORK_REQUEST);
|
||||
assertPendingIntentRequestMatches(request, secondRequest, useListen);
|
||||
receivedCount.incrementAndGet();
|
||||
networkFuture.complete(intent.getParcelableExtra(EXTRA_NETWORK));
|
||||
}
|
||||
};
|
||||
mContext.registerReceiver(receiver, filter);
|
||||
|
||||
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
|
||||
try {
|
||||
assertEquals(wifiNetwork, networkFuture.get(
|
||||
NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
} catch (TimeoutException e) {
|
||||
throw new AssertionError("PendingIntent not received for " + secondRequest, e);
|
||||
}
|
||||
|
||||
// Sleep for a small amount of time to try to check that only one callback is ever
|
||||
// received (so the first callback was really unregistered). This does not guarantee
|
||||
// that the test will fail if it runs very slowly, but it should at least be very
|
||||
// noticeably flaky.
|
||||
Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
|
||||
|
||||
// TODO: BUG (b/189868426): this should also apply to listens
|
||||
if (!useListen) {
|
||||
assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
|
||||
}
|
||||
} finally {
|
||||
if (firstIntent != null) mCm.unregisterNetworkCallback(firstIntent);
|
||||
if (secondIntent != null) mCm.unregisterNetworkCallback(secondIntent);
|
||||
if (receiver != null) mContext.unregisterReceiver(receiver);
|
||||
mCtsNetUtils.ensureWifiConnected();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed,
|
||||
boolean useListen) {
|
||||
// TODO: BUG (b/191713869): on S the request extra is null on listens
|
||||
if (isAtLeastS() && useListen && broadcasted == null) return;
|
||||
assertArrayEquals(filed.networkCapabilities.getCapabilities(),
|
||||
broadcasted.networkCapabilities.getCapabilities());
|
||||
// TODO: BUG (b/189868426): this should also apply to listens
|
||||
if (useListen) return;
|
||||
assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
|
||||
broadcasted.networkCapabilities.getTransportTypes());
|
||||
}
|
||||
|
||||
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
|
||||
@Test
|
||||
public void testRegisterNetworkRequest_identicalPendingIntents() throws Exception {
|
||||
runIdenticalPendingIntentsRequestTest(false /* useListen */);
|
||||
}
|
||||
|
||||
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
|
||||
@Test
|
||||
public void testRegisterNetworkCallback_identicalPendingIntents() throws Exception {
|
||||
runIdenticalPendingIntentsRequestTest(true /* useListen */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exercises the requestNetwork with NetworkCallback API. This checks to
|
||||
* see if we get a callback for an INTERNET request.
|
||||
|
||||
Reference in New Issue
Block a user