diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 8107be3b0f..a7e6a2e86f 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -242,6 +242,8 @@ import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; +import androidx.annotation.RequiresApi; + import com.android.connectivity.resources.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -262,6 +264,10 @@ import com.android.net.module.util.PerUidCounter; import com.android.net.module.util.PermissionUtils; import com.android.net.module.util.TcUtils; import com.android.net.module.util.netlink.InetDiagMessage; +import com.android.networkstack.apishim.BroadcastOptionsShimImpl; +import com.android.networkstack.apishim.ConstantsShim; +import com.android.networkstack.apishim.common.BroadcastOptionsShim; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.CarrierPrivilegeAuthenticator; import com.android.server.connectivity.ClatCoordinator; @@ -372,6 +378,10 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int DEFAULT_LINGER_DELAY_MS = 30_000; private static final int DEFAULT_NASCENT_DELAY_MS = 5_000; + // Delimiter used when creating the broadcast delivery group for sending + // CONNECTIVITY_ACTION broadcast. + private static final char DELIVERY_GROUP_KEY_DELIMITER = ';'; + // The maximum value for the blocking validation result, in milliseconds. public static final int MAX_VALIDATION_IGNORE_AFTER_ROAM_TIME_MS = 10000; @@ -1411,6 +1421,16 @@ public class ConnectivityService extends IConnectivityManager.Stub + ", ingress=true, PRIO_POLICE, ETH_P_ALL) failure: ", e); } } + + /** + * Wraps {@link BroadcastOptionsShimImpl#newInstance(BroadcastOptions)} + */ + // TODO: when available in all active branches: + // @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @RequiresApi(Build.VERSION_CODES.CUR_DEVELOPMENT) + public BroadcastOptionsShim makeBroadcastOptionsShim(BroadcastOptions options) { + return BroadcastOptionsShimImpl.newInstance(options); + } } public ConnectivityService(Context context) { @@ -3037,6 +3057,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ConnectivityManager.EXTRA_NETWORK_INFO); final BroadcastOptions opts = BroadcastOptions.makeBasic(); opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M); + applyMostRecentPolicyForConnectivityAction(opts, ni); options = opts.toBundle(); intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); } @@ -3048,6 +3069,32 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void applyMostRecentPolicyForConnectivityAction(BroadcastOptions options, + NetworkInfo info) { + // Delivery group policy APIs are only available on U+. + if (!SdkLevel.isAtLeastU()) return; + + final BroadcastOptionsShim optsShim = mDeps.makeBroadcastOptionsShim(options); + try { + // This allows us to discard older broadcasts still waiting to be delivered + // which have the same namespace and key. + optsShim.setDeliveryGroupPolicy(ConstantsShim.DELIVERY_GROUP_POLICY_MOST_RECENT); + optsShim.setDeliveryGroupMatchingKey(ConnectivityManager.CONNECTIVITY_ACTION, + createDeliveryGroupKeyForConnectivityAction(info)); + } catch (UnsupportedApiLevelException e) { + Log.wtf(TAG, "Using unsupported API" + e); + } + } + + @VisibleForTesting + static String createDeliveryGroupKeyForConnectivityAction(NetworkInfo info) { + final StringBuilder sb = new StringBuilder(); + sb.append(info.getType()).append(DELIVERY_GROUP_KEY_DELIMITER); + sb.append(info.getSubtype()).append(DELIVERY_GROUP_KEY_DELIMITER); + sb.append(info.getExtraInfo()); + return sb.toString(); + } + /** * Called by SystemServer through ConnectivityManager when the system is ready. */ diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index ca6a14b3c6..7d64c263dd 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -151,6 +151,7 @@ import static com.android.server.ConnectivityService.PREFERENCE_ORDER_MOBILE_DAT import static com.android.server.ConnectivityService.PREFERENCE_ORDER_OEM; import static com.android.server.ConnectivityService.PREFERENCE_ORDER_PROFILE; import static com.android.server.ConnectivityService.PREFERENCE_ORDER_VPN; +import static com.android.server.ConnectivityService.createDeliveryGroupKeyForConnectivityAction; import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister; import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister; @@ -210,6 +211,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; @@ -360,6 +362,8 @@ import com.android.net.module.util.LocationPermissionChecker; import com.android.net.module.util.NetworkMonitorUtils; import com.android.networkstack.apishim.ConstantsShim; import com.android.networkstack.apishim.NetworkAgentConfigShimImpl; +import com.android.networkstack.apishim.common.BroadcastOptionsShim; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.ConnectivityService.NetworkRequestInfo; import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces; @@ -574,6 +578,7 @@ public class ConnectivityServiceTest { @Mock BpfNetMaps mBpfNetMaps; @Mock CarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator; @Mock TetheringManager mTetheringManager; + @Mock BroadcastOptionsShim mBroadcastOptionsShim; // BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the // underlying binder calls. @@ -821,6 +826,25 @@ public class ConnectivityServiceTest { // null should not pass the test return null; } + + @Override + public void sendStickyBroadcast(Intent intent, Bundle options) { + // Verify that delivery group policy APIs were used on U. + if (SdkLevel.isAtLeastU() && CONNECTIVITY_ACTION.equals(intent.getAction())) { + final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO, + NetworkInfo.class); + try { + verify(mBroadcastOptionsShim).setDeliveryGroupPolicy( + eq(ConstantsShim.DELIVERY_GROUP_POLICY_MOST_RECENT)); + verify(mBroadcastOptionsShim).setDeliveryGroupMatchingKey( + eq(CONNECTIVITY_ACTION), + eq(createDeliveryGroupKeyForConnectivityAction(ni))); + } catch (UnsupportedApiLevelException e) { + throw new RuntimeException(e); + } + } + super.sendStickyBroadcast(intent, options); + } } // This was only added in the T SDK, but this test needs to build against the R+S SDKs, too. @@ -2053,6 +2077,12 @@ public class ConnectivityServiceTest { assertNotEquals(-1L, (long) mActiveRateLimit.getOrDefault(iface, -1L)); mActiveRateLimit.put(iface, -1L); } + + @Override + public BroadcastOptionsShim makeBroadcastOptionsShim(BroadcastOptions options) { + reset(mBroadcastOptionsShim); + return mBroadcastOptionsShim; + } } private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) { @@ -17353,4 +17383,14 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mMockNetd).interfaceSetMtu(eq(ifaceName2), eq(mtu)); } + + @Test + public void testCreateDeliveryGroupKeyForConnectivityAction() throws Exception { + final NetworkInfo info = new NetworkInfo(0 /* type */, 2 /* subtype */, + "MOBILE" /* typeName */, "LTE" /* subtypeName */); + assertEquals("0;2;null", createDeliveryGroupKeyForConnectivityAction(info)); + + info.setExtraInfo("test_info"); + assertEquals("0;2;test_info", createDeliveryGroupKeyForConnectivityAction(info)); + } }