diff options
32 files changed, 1560 insertions, 345 deletions
diff --git a/Android.bp b/Android.bp index 402cf1c245ab..6a0bdc3f7fde 100644 --- a/Android.bp +++ b/Android.bp @@ -574,6 +574,7 @@ java_library { ], sdk_version: "core_platform", static_libs: [ + "bouncycastle-repackaged-unbundled", "framework-internal-utils", // If MimeMap ever becomes its own APEX, then this dependency would need to be removed // in favor of an API stubs dependency in java_library "framework" below. diff --git a/core/api/current.txt b/core/api/current.txt index 285506f0556d..f453ccaf75ad 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1142,6 +1142,7 @@ package android { field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 + field public static final int requireDeviceScreenOn = 16844312; // 0x1010618 field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 24a6d3d9ae22..72fcc72169c3 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9263,6 +9263,33 @@ package android.telecom { field @Deprecated public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5 } + public final class BluetoothCallQualityReport implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getNegativeAcknowledgementCount(); + method @IntRange(from=0) public int getPacketsNotReceivedCount(); + method @IntRange(from=0) public int getRetransmittedPacketsCount(); + method @IntRange(from=0xffffff81, to=20) public int getRssiDbm(); + method public long getSentTimestampMillis(); + method public int getSnrDb(); + method public boolean isChoppyVoice(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.BluetoothCallQualityReport> CREATOR; + field public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT"; + field public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT"; + } + + public static final class BluetoothCallQualityReport.Builder { + ctor public BluetoothCallQualityReport.Builder(); + method @NonNull public android.telecom.BluetoothCallQualityReport build(); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setChoppyVoice(boolean); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setNegativeAcknowledgementCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setPacketsNotReceivedCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRetransmittedPacketsCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRssiDbm(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSentTimestampMillis(long); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSnrDb(int); + } + public final class Call { method @Deprecated public void addListener(android.telecom.Call.Listener); method public void enterBackgroundAudioProcessing(); @@ -11962,6 +11989,7 @@ package android.telephony.ims { field public static final String IPTYPE_IPV6 = "IPV6"; field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; @@ -11994,6 +12022,7 @@ package android.telephony.ims { field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; field public static final String SIP_TRANSPORT_TCP = "TCP"; field public static final String SIP_TRANSPORT_UDP = "UDP"; } diff --git a/core/java/android/app/smartspace/OWNERS b/core/java/android/app/smartspace/OWNERS new file mode 100644 index 000000000000..19ef9d774e6a --- /dev/null +++ b/core/java/android/app/smartspace/OWNERS @@ -0,0 +1,2 @@ +srazdan@google.com +alexmang@google.com
\ No newline at end of file diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java index 250cff29944a..a22d41a5ef86 100644 --- a/core/java/android/net/http/SslCertificate.java +++ b/core/java/android/net/http/SslCertificate.java @@ -26,7 +26,7 @@ import android.view.View; import android.widget.TextView; import com.android.internal.util.HexDump; -import com.android.org.bouncycastle.asn1.x509.X509Name; +import com.android.internal.org.bouncycastle.asn1.x509.X509Name; import java.io.ByteArrayInputStream; import java.math.BigInteger; diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 33beb6a9d188..fa090f59a8b9 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -67,7 +67,6 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); - /** @hide */ @VisibleForTesting public static final Map< VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index d7c2e0522b0f..64ab074f397a 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -97,6 +97,11 @@ public final class ApduServiceInfo implements Parcelable { final boolean mRequiresDeviceUnlock; /** + * Whether this service should only be started when the device is screen on. + */ + final boolean mRequiresDeviceScreenOn; + + /** * The id of the service banner specified in XML. */ final int mBannerResourceId; @@ -119,6 +124,18 @@ public final class ApduServiceInfo implements Parcelable { ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { + this(info, onHost, description, staticAidGroups, dynamicAidGroups, + requiresUnlock, onHost ? true : false, bannerResource, uid, + settingsActivityName, offHost, staticOffHost); + } + + /** + * @hide + */ + public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, + boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, + String settingsActivityName, String offHost, String staticOffHost) { this.mService = info; this.mDescription = description; this.mStaticAidGroups = new HashMap<String, AidGroup>(); @@ -127,6 +144,7 @@ public final class ApduServiceInfo implements Parcelable { this.mStaticOffHostName = staticOffHost; this.mOnHost = onHost; this.mRequiresDeviceUnlock = requiresUnlock; + this.mRequiresDeviceScreenOn = requiresScreenOn; for (AidGroup aidGroup : staticAidGroups) { this.mStaticAidGroups.put(aidGroup.category, aidGroup); } @@ -183,6 +201,9 @@ public final class ApduServiceInfo implements Parcelable { mRequiresDeviceUnlock = sa.getBoolean( com.android.internal.R.styleable.HostApduService_requireDeviceUnlock, false); + mRequiresDeviceScreenOn = sa.getBoolean( + com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn, + true); mBannerResourceId = sa.getResourceId( com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1); mSettingsActivityName = sa.getString( @@ -196,7 +217,12 @@ public final class ApduServiceInfo implements Parcelable { mService = info; mDescription = sa.getString( com.android.internal.R.styleable.OffHostApduService_description); - mRequiresDeviceUnlock = false; + mRequiresDeviceUnlock = sa.getBoolean( + com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock, + false); + mRequiresDeviceScreenOn = sa.getBoolean( + com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn, + false); mBannerResourceId = sa.getResourceId( com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1); mSettingsActivityName = sa.getString( @@ -419,6 +445,13 @@ public final class ApduServiceInfo implements Parcelable { return mRequiresDeviceUnlock; } + /** + * Returns whether this service should only be started when the device is screen on. + */ + public boolean requiresScreenOn() { + return mRequiresDeviceScreenOn; + } + @UnsupportedAppUsage public String getDescription() { return mDescription; @@ -542,6 +575,7 @@ public final class ApduServiceInfo implements Parcelable { dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values())); } dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); + dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0); dest.writeInt(mBannerResourceId); dest.writeInt(mUid); dest.writeString(mSettingsActivityName); @@ -568,11 +602,12 @@ public final class ApduServiceInfo implements Parcelable { source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); } boolean requiresUnlock = source.readInt() != 0; + boolean requiresScreenOn = source.readInt() != 0; int bannerResource = source.readInt(); int uid = source.readInt(); String settingsActivityName = source.readString(); return new ApduServiceInfo(info, onHost, description, staticAidGroups, - dynamicAidGroups, requiresUnlock, bannerResource, uid, + dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, settingsActivityName, offHostName, staticOffHostName); } @@ -607,6 +642,8 @@ public final class ApduServiceInfo implements Parcelable { } } pw.println(" Settings Activity: " + mSettingsActivityName); + pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock); + pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); } /** diff --git a/core/java/android/service/smartspace/OWNERS b/core/java/android/service/smartspace/OWNERS new file mode 100644 index 000000000000..19ef9d774e6a --- /dev/null +++ b/core/java/android/service/smartspace/OWNERS @@ -0,0 +1,2 @@ +srazdan@google.com +alexmang@google.com
\ No newline at end of file diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 023d9ff28f41..20230e770bf5 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -98,9 +98,17 @@ public abstract class CookieManager { public abstract boolean acceptThirdPartyCookies(WebView webview); /** - * Sets a cookie for the given URL. Any existing cookie with the same host, - * path and name will be replaced with the new cookie. The cookie being set - * will be ignored if it is expired. + * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same + * host, path and name will be replaced with the new cookie. The cookie being set + * will be ignored if it is expired. To set multiple cookies, your application should invoke + * this method multiple times. + * + * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP + * response header defined by + * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>. + * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of + * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please + * consult the RFC specification for a list of valid attributes. * * <p class="note"><b>Note:</b> if specifying a {@code value} containing the {@code "Secure"} * attribute, {@code url} must use the {@code "https://"} scheme. @@ -112,13 +120,20 @@ public abstract class CookieManager { public abstract void setCookie(String url, String value); /** - * Sets a cookie for the given URL. Any existing cookie with the same host, - * path and name will be replaced with the new cookie. The cookie being set - * will be ignored if it is expired. - * <p> - * This method is asynchronous. - * If a {@link ValueCallback} is provided, - * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current + * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same + * host, path and name will be replaced with the new cookie. The cookie being set + * will be ignored if it is expired. To set multiple cookies, your application should invoke + * this method multiple times. + * + * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP + * response header defined by + * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>. + * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of + * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please + * consult the RFC specification for a list of valid attributes. + * + * <p>This method is asynchronous. If a {@link ValueCallback} is provided, + * {@link ValueCallback#onReceiveValue} will be called on the current * thread's {@link android.os.Looper} once the operation is complete. * The value provided to the callback indicates whether the cookie was set successfully. * You can pass {@code null} as the callback if you don't need to know when the operation @@ -137,7 +152,10 @@ public abstract class CookieManager { callback); /** - * Gets the cookies for the given URL. + * Gets all the cookies for the given URL. This may return multiple key-value pairs if multiple + * cookies are associated with this URL, in which case each cookie will be delimited by {@code + * "; "} characters (semicolon followed by a space). Each key-value pair will be of the form + * {@code "key=value"}. * * @param url the URL for which the cookies are requested * @return value the cookies as a string, using the format of the 'Cookie' diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a668e8e6dd0f..0e80c7b19224 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1673,7 +1673,7 @@ @SystemApi <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" - android:protectionLevel="signature|setup" /> + android:protectionLevel="signature|privileged" /> <!-- @SystemApi @hide Allows applications to toggle airplane mode. <p>Not for use by third-party or privileged applications. diff --git a/core/res/OWNERS b/core/res/OWNERS index a30111b44382..9d739b90bcc5 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -6,9 +6,12 @@ dsandler@google.com dupin@google.com hackbod@android.com hackbod@google.com +ilyamaty@google.com +jaggies@google.com jsharkey@android.com jsharkey@google.com juliacr@google.com +kchyn@google.com michaelwr@google.com nandana@google.com narayan@google.com diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 4b3d82a04b8b..9cc0690126e9 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3856,6 +3856,9 @@ <!-- Component name of an activity that allows the user to modify the settings for this service. --> <attr name="settingsActivity"/> + <!-- Whether the device must be screen on before routing data to this service. + The default is true.--> + <attr name="requireDeviceScreenOn" format="boolean"/> </declare-styleable> <!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that @@ -3874,6 +3877,12 @@ <attr name="settingsActivity"/> <!-- Secure Element which the AIDs should be routed to --> <attr name="secureElementName" format="string"/> + <!-- Whether the device must be unlocked before routing data to this service. + The default is false.--> + <attr name="requireDeviceUnlock"/> + <!-- Whether the device must be screen on before routing data to this service. + The default is false.--> + <attr name="requireDeviceScreenOn"/> </declare-styleable> <!-- Specify one or more <code>aid-group</code> elements inside a diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a0be0681bd38..0874a77815b5 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3045,6 +3045,7 @@ <public-group type="attr" first-id="0x01010617"> <public name="canPauseRecording" /> <!-- attribute definitions go here --> + <public name="requireDeviceScreenOn" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 7abcfdc98bc6..9e1fb54bedbe 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -19,9 +19,9 @@ package android.security; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; -import com.android.org.bouncycastle.util.io.pem.PemObject; -import com.android.org.bouncycastle.util.io.pem.PemReader; -import com.android.org.bouncycastle.util.io.pem.PemWriter; +import com.android.internal.org.bouncycastle.util.io.pem.PemObject; +import com.android.internal.org.bouncycastle.util.io.pem.PemReader; +import com.android.internal.org.bouncycastle.util.io.pem.PemWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 4a67135227dd..e19d88c182ff 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -45,8 +45,8 @@ import android.security.keystore.KeystoreResponse; import android.security.keystore.UserNotAuthenticatedException; import android.util.Log; -import com.android.org.bouncycastle.asn1.ASN1InputStream; -import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; +import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 6ad8d2c0aca3..334b1110d651 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -26,24 +26,24 @@ import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterDefs; -import com.android.org.bouncycastle.asn1.ASN1EncodableVector; -import com.android.org.bouncycastle.asn1.ASN1InputStream; -import com.android.org.bouncycastle.asn1.ASN1Integer; -import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; -import com.android.org.bouncycastle.asn1.DERBitString; -import com.android.org.bouncycastle.asn1.DERNull; -import com.android.org.bouncycastle.asn1.DERSequence; -import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import com.android.org.bouncycastle.asn1.x509.Certificate; -import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import com.android.org.bouncycastle.asn1.x509.TBSCertificate; -import com.android.org.bouncycastle.asn1.x509.Time; -import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; -import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; -import com.android.org.bouncycastle.jce.X509Principal; -import com.android.org.bouncycastle.jce.provider.X509CertificateObject; -import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; +import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector; +import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; +import com.android.internal.org.bouncycastle.asn1.ASN1Integer; +import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier; +import com.android.internal.org.bouncycastle.asn1.DERBitString; +import com.android.internal.org.bouncycastle.asn1.DERNull; +import com.android.internal.org.bouncycastle.asn1.DERSequence; +import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import com.android.internal.org.bouncycastle.asn1.x509.Certificate; +import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate; +import com.android.internal.org.bouncycastle.asn1.x509.Time; +import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; +import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import com.android.internal.org.bouncycastle.jce.X509Principal; +import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject; +import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator; import libcore.util.EmptyArray; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c091dfa384ca..96c3e573a8f5 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -739,11 +739,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type, - boolean isDefaultNetwork) { + boolean isFallbackNetwork) { if (DBG) { log("Sending " + state + " broadcast for type " + type + " " + nai.toShortString() - + " isDefaultNetwork=" + isDefaultNetwork); + + " isFallbackNetwork=" + isFallbackNetwork); } } @@ -762,10 +762,10 @@ public class ConnectivityService extends IConnectivityManager.Stub list.add(nai); } - // Send a broadcast if this is the first network of its type or if it's the default. - final boolean isDefaultNetwork = mService.isDefaultNetwork(nai); - if ((list.size() == 1) || isDefaultNetwork) { - maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork); + // Send a broadcast if this is the first network of its type or if it's the fallback. + final boolean isFallbackNetwork = mService.isFallbackNetwork(nai); + if ((list.size() == 1) || isFallbackNetwork) { + maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isFallbackNetwork); mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type); } } @@ -794,7 +794,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ", sending connected broadcast"); final NetworkAgentInfo replacement = list.get(0); maybeLogBroadcast(replacement, DetailedState.CONNECTED, type, - mService.isDefaultNetwork(replacement)); + mService.isFallbackNetwork(replacement)); mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type); } } @@ -810,14 +810,14 @@ public class ConnectivityService extends IConnectivityManager.Stub // send out another legacy broadcast - currently only used for suspend/unsuspend // toggle public void update(NetworkAgentInfo nai) { - final boolean isDefault = mService.isDefaultNetwork(nai); + final boolean isFallback = mService.isFallbackNetwork(nai); final DetailedState state = nai.networkInfo.getDetailedState(); for (int type = 0; type < mTypeLists.length; type++) { final ArrayList<NetworkAgentInfo> list = mTypeLists[type]; final boolean contains = (list != null && list.contains(nai)); final boolean isFirst = contains && (nai == list.get(0)); - if (isFirst || contains && isDefault) { - maybeLogBroadcast(nai, state, type, isDefault); + if (isFirst || contains && isFallback) { + maybeLogBroadcast(nai, state, type, isFallback); mService.sendLegacyNetworkBroadcast(nai, state, type); } } @@ -1021,11 +1021,13 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID); mMetricsLog = logger; - mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); mNetworkRanker = new NetworkRanker(); - NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder()); - mNetworkRequests.put(mDefaultRequest, defaultNRI); - mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI); + final NetworkRequest fallbackRequest = createDefaultInternetRequestForTransport( + -1, NetworkRequest.Type.REQUEST); + mFallbackRequest = new NetworkRequestInfo(null, fallbackRequest, new Binder()); + mNetworkRequests.put(fallbackRequest, mFallbackRequest); + mDefaultNetworkRequests.add(mFallbackRequest); + mNetworkRequestInfoLogs.log("REGISTER " + mFallbackRequest); mDefaultMobileDataRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); @@ -1364,7 +1366,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkState getUnfilteredActiveNetworkState(int uid) { - NetworkAgentInfo nai = getDefaultNetwork(); + NetworkAgentInfo nai = getFallbackNetwork(); final Network[] networks = getVpnUnderlyingNetworks(uid); if (networks != null) { @@ -1497,7 +1499,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - NetworkAgentInfo nai = getDefaultNetwork(); + NetworkAgentInfo nai = getFallbackNetwork(); if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, ignoreBlocked)) { return null; @@ -1636,7 +1638,7 @@ public class ConnectivityService extends IConnectivityManager.Stub HashMap<Network, NetworkCapabilities> result = new HashMap<>(); - NetworkAgentInfo nai = getDefaultNetwork(); + final NetworkAgentInfo nai = getFallbackNetwork(); NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai); if (nc != null) { result.put( @@ -2023,7 +2025,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one // callback from each caller type. Need to re-factor NetdEventListenerService to allow // multiple NetworkMonitor registrants. - if (nai != null && nai.satisfies(mDefaultRequest)) { + if (nai != null && nai.satisfies(mFallbackRequest.mRequests.get(0))) { nai.networkMonitor().notifyDnsResponse(returnCode); } } @@ -2580,12 +2582,12 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); pw.println(); - final NetworkAgentInfo defaultNai = getDefaultNetwork(); + final NetworkAgentInfo fallbackNai = getFallbackNetwork(); pw.print("Active default network: "); - if (defaultNai == null) { + if (fallbackNai == null) { pw.println("none"); } else { - pw.println(defaultNai.network.getNetId()); + pw.println(fallbackNai.network.getNetId()); } pw.println(); @@ -2968,7 +2970,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0); final boolean wasValidated = nai.lastValidated; - final boolean wasDefault = isDefaultNetwork(nai); + final boolean wasFallback = isFallbackNetwork(nai); if (DBG) { final String logMsg = !TextUtils.isEmpty(redirectUrl) @@ -2977,7 +2979,7 @@ public class ConnectivityService extends IConnectivityManager.Stub log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg); } if (valid != nai.lastValidated) { - if (wasDefault) { + if (wasFallback) { mMetricsLog.logDefaultNetworkValidity(valid); } final int oldScore = nai.getCurrentScore(); @@ -3353,13 +3355,13 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("Error connecting NetworkAgent"); mNetworkAgentInfos.remove(nai); if (nai != null) { - final boolean wasDefault = isDefaultNetwork(nai); + final boolean wasFallback = isFallbackNetwork(nai); synchronized (mNetworkForNetId) { mNetworkForNetId.remove(nai.network.getNetId()); } mNetIdManager.releaseNetId(nai.network.getNetId()); // Just in case. - mLegacyTypeTracker.remove(nai, wasDefault); + mLegacyTypeTracker.remove(nai, wasFallback); } } } @@ -3398,8 +3400,8 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); } - final boolean wasDefault = isDefaultNetwork(nai); - if (wasDefault) { + final boolean wasFallback = isFallbackNetwork(nai); + if (wasFallback) { mDefaultInetConditionPublished = 0; // Log default network disconnection before required book-keeping. // Let rematchAllNetworksAndRequests() below record a new default network event @@ -3441,19 +3443,24 @@ public class ConnectivityService extends IConnectivityManager.Stub && currentNetwork.network.getNetId() == nai.network.getNetId()) { nri.setSatisfier(null, null); sendUpdatedScoreToFactories(request, null); + + if (mFallbackRequest == nri) { + // TODO : make battery stats aware that since 2013 multiple interfaces may be + // active at the same time. For now keep calling this with the fallback + // network, because while incorrect this is the closest to the old (also + // incorrect) behavior. + mNetworkActivityTracker.updateDataActivityTracking( + null /* newNetwork */, nai); + notifyLockdownVpn(nai); + ensureNetworkTransitionWakelock(nai.toShortString()); + } } } nai.clearLingerState(); - // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given - // there's a full rematch right after. Currently, deleting it breaks tests that check for - // the default network disconnecting. Find out why, fix the rematch code, and delete this. - if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { - mDefaultNetworkNai = null; - mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai); - notifyLockdownVpn(nai); - ensureNetworkTransitionWakelock(nai.toShortString()); - } - mLegacyTypeTracker.remove(nai, wasDefault); + // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after. + // Currently, deleting it breaks tests that check for the fallback network disconnecting. + // Find out why, fix the rematch code, and delete this. + mLegacyTypeTracker.remove(nai, wasFallback); rematchAllNetworksAndRequests(); mLingerMonitor.noteDisconnect(nai); if (nai.created) { @@ -4253,7 +4260,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest getDefaultRequest() { - return mDefaultRequest; + return mFallbackRequest.mRequests.get(0); } private class InternalHandler extends Handler { @@ -4499,7 +4506,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event. final NetworkAgentInfo nai; if (network == null) { - nai = getDefaultNetwork(); + nai = getFallbackNetwork(); } else { nai = getNetworkAgentInfoForNetwork(network); } @@ -4518,7 +4525,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Network network, int uid, boolean hasConnectivity) { final NetworkAgentInfo nai; if (network == null) { - nai = getDefaultNetwork(); + nai = getFallbackNetwork(); } else { nai = getNetworkAgentInfoForNetwork(network); } @@ -4884,7 +4891,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret // the underlyingNetworks list. if (underlyingNetworks == null) { - NetworkAgentInfo defaultNai = getDefaultNetwork(); + final NetworkAgentInfo defaultNai = getFallbackNetwork(); if (defaultNai != null) { underlyingNetworks = new Network[] { defaultNai.network }; } @@ -4936,7 +4943,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) { - final Network defaultNetwork = getNetwork(getDefaultNetwork()); + final Network defaultNetwork = getNetwork(getFallbackNetwork()); if (underlyingNetworks == null && defaultNetwork != null) { // null underlying networks means to track the default. underlyingNetworks = new Network[] { defaultNetwork }; @@ -5493,6 +5500,8 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @VisibleForTesting protected class NetworkRequestInfo implements IBinder.DeathRecipient { + // The requests to be satisfied in priority order. Non-multilayer requests will only have a + // single NetworkRequest in mRequests. final List<NetworkRequest> mRequests; // mSatisfier and mActiveRequest rely on one another therefore set them together. @@ -6038,11 +6047,13 @@ public class ConnectivityService extends IConnectivityManager.Stub @GuardedBy("mBlockedAppUids") private final HashSet<Integer> mBlockedAppUids = new HashSet<>(); + // The always-on request for an Internet-capable network that apps without a specific default + // fall back to. @NonNull - private final NetworkRequest mDefaultRequest; - // The NetworkAgentInfo currently satisfying the default request, if any. - @Nullable - private volatile NetworkAgentInfo mDefaultNetworkNai = null; + private final NetworkRequestInfo mFallbackRequest; + // Collection of NetworkRequestInfo's used for default networks. + @NonNull + private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>(); // Request used to optionally keep mobile data active even when higher // priority networks like Wi-Fi are active. @@ -6055,8 +6066,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // Request used to optionally keep vehicle internal network always active private final NetworkRequest mDefaultVehicleRequest; - private NetworkAgentInfo getDefaultNetwork() { - return mDefaultNetworkNai; + // TODO: b/178729499 update this in favor of a method taking in a UID. + // The NetworkAgentInfo currently satisfying the fallback request, if any. + private NetworkAgentInfo getFallbackNetwork() { + return mFallbackRequest.mSatisfier; } @Nullable @@ -6073,8 +6086,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } @VisibleForTesting - protected boolean isDefaultNetwork(NetworkAgentInfo nai) { - return nai == getDefaultNetwork(); + protected boolean isFallbackNetwork(NetworkAgentInfo nai) { + return nai == getFallbackNetwork(); } // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent @@ -6143,8 +6156,8 @@ public class ConnectivityService extends IConnectivityManager.Stub LinkProperties lp = new LinkProperties(linkProperties); - // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network - // satisfies mDefaultRequest. + // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network + // satisfies mFallbackRequest. final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkAgentInfo nai = new NetworkAgentInfo(na, new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, @@ -6221,7 +6234,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // for (LinkProperties lp : newLp.getStackedLinks()) { // updateMtu(lp, null); // } - if (isDefaultNetwork(networkAgent)) { + if (isFallbackNetwork(networkAgent)) { updateTcpBufferSizes(newLp.getTcpBufferSizes()); } @@ -6233,7 +6246,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // updateDnses will fetch the private DNS configuration from DnsManager. mDnsManager.updatePrivateDnsStatus(netId, newLp); - if (isDefaultNetwork(networkAgent)) { + if (isFallbackNetwork(networkAgent)) { handleApplyDefaultProxy(newLp.getHttpProxy()); } else { updateProxy(newLp, oldLp); @@ -7184,14 +7197,41 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) { - if (DBG) log("Switching to new default network: " + newNetwork); + private void processDefaultNetworkChanges(@NonNull final NetworkReassignment changes) { + boolean isDefaultChanged = false; + for (final NetworkRequestInfo defaultRequestInfo : mDefaultNetworkRequests) { + final NetworkReassignment.RequestReassignment reassignment = + changes.getReassignment(defaultRequestInfo); + if (null == reassignment) { + continue; + } + // reassignment only contains those instances where the satisfying network changed. + isDefaultChanged = true; + // Notify system services of the new default. + makeDefault(defaultRequestInfo, reassignment.mOldNetwork, reassignment.mNewNetwork); + } + + if (isDefaultChanged) { + // Hold a wakelock for a short time to help apps in migrating to a new default. + scheduleReleaseNetworkTransitionWakelock(); + } + } - mDefaultNetworkNai = newNetwork; + private void makeDefault(@NonNull final NetworkRequestInfo nri, + @Nullable final NetworkAgentInfo oldDefaultNetwork, + @Nullable final NetworkAgentInfo newDefaultNetwork) { + if (DBG) { + log("Switching to new default network for: " + nri + " using " + newDefaultNetwork); + } try { - if (null != newNetwork) { - mNetd.networkSetDefault(newNetwork.network.getNetId()); + // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes. + if (mFallbackRequest != nri) { + return; + } + + if (null != newDefaultNetwork) { + mNetd.networkSetDefault(newDefaultNetwork.network.getNetId()); } else { mNetd.networkClearDefault(); } @@ -7199,16 +7239,41 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("Exception setting default network :" + e); } - notifyLockdownVpn(newNetwork); - handleApplyDefaultProxy(null != newNetwork - ? newNetwork.linkProperties.getHttpProxy() : null); - updateTcpBufferSizes(null != newNetwork - ? newNetwork.linkProperties.getTcpBufferSizes() : null); + if (oldDefaultNetwork != null) { + mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork); + } + mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork); + notifyLockdownVpn(newDefaultNetwork); + handleApplyDefaultProxy(null != newDefaultNetwork + ? newDefaultNetwork.linkProperties.getHttpProxy() : null); + updateTcpBufferSizes(null != newDefaultNetwork + ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null); notifyIfacesChangedForNetworkStats(); // Fix up the NetworkCapabilities of any networks that have this network as underlying. - if (newNetwork != null) { - propagateUnderlyingNetworkCapabilities(newNetwork.network); + if (newDefaultNetwork != null) { + propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network); } + + // Log 0 -> X and Y -> X default network transitions, where X is the new default. + final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null; + final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0; + final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated; + final LinkProperties lp = (newDefaultNetwork != null) + ? newDefaultNetwork.linkProperties : null; + final NetworkCapabilities nc = (newDefaultNetwork != null) + ? newDefaultNetwork.networkCapabilities : null; + + final Network prevNetwork = (oldDefaultNetwork != null) + ? oldDefaultNetwork.network : null; + final int prevScore = (oldDefaultNetwork != null) + ? oldDefaultNetwork.getCurrentScore() : 0; + final LinkProperties prevLp = (oldDefaultNetwork != null) + ? oldDefaultNetwork.linkProperties : null; + final NetworkCapabilities prevNc = (oldDefaultNetwork != null) + ? oldDefaultNetwork.networkCapabilities : null; + + mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc, + prevNetwork, prevScore, prevLp, prevNc); } private void processListenRequests(@NonNull final NetworkAgentInfo nai) { @@ -7460,46 +7525,8 @@ public class ConnectivityService extends IConnectivityManager.Stub now); } - final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork(); - final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest); - final NetworkReassignment.RequestReassignment reassignment = - changes.getReassignment(defaultRequestInfo); - final NetworkAgentInfo newDefaultNetwork = - null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork; - - if (oldDefaultNetwork != newDefaultNetwork) { - if (oldDefaultNetwork != null) { - mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork); - } - mNetworkActivityTracker.updateDataActivityTracking( - newDefaultNetwork, oldDefaultNetwork); - // Notify system services of the new default. - makeDefault(newDefaultNetwork); - - // Log 0 -> X and Y -> X default network transitions, where X is the new default. - final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null; - final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0; - final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated; - final LinkProperties lp = (newDefaultNetwork != null) - ? newDefaultNetwork.linkProperties : null; - final NetworkCapabilities nc = (newDefaultNetwork != null) - ? newDefaultNetwork.networkCapabilities : null; - - final Network prevNetwork = (oldDefaultNetwork != null) - ? oldDefaultNetwork.network : null; - final int prevScore = (oldDefaultNetwork != null) - ? oldDefaultNetwork.getCurrentScore() : 0; - final LinkProperties prevLp = (oldDefaultNetwork != null) - ? oldDefaultNetwork.linkProperties : null; - final NetworkCapabilities prevNc = (oldDefaultNetwork != null) - ? oldDefaultNetwork.networkCapabilities : null; - - mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc, - prevNetwork, prevScore, prevLp, prevNc); - - // Have a new default network, release the transition wakelock in - scheduleReleaseNetworkTransitionWakelock(); - } + // Process default network changes if applicable. + processDefaultNetworkChanges(changes); // Notify requested networks are available after the default net is switched, but // before LegacyTypeTracker sends legacy broadcasts @@ -7552,7 +7579,7 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyNetworkLosing(nai, now); } - updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais); + updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais); // Tear down all unneeded networks. for (NetworkAgentInfo nai : mNetworkAgentInfos) { @@ -7595,29 +7622,36 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void updateLegacyTypeTrackerAndVpnLockdownForRematch( - @Nullable final NetworkAgentInfo oldDefaultNetwork, - @Nullable final NetworkAgentInfo newDefaultNetwork, + @NonNull final NetworkReassignment changes, @NonNull final Collection<NetworkAgentInfo> nais) { - if (oldDefaultNetwork != newDefaultNetwork) { + final NetworkReassignment.RequestReassignment fallbackReassignment = + changes.getReassignment(mFallbackRequest); + final NetworkAgentInfo oldFallbackNetwork = + null != fallbackReassignment ? fallbackReassignment.mOldNetwork : null; + final NetworkAgentInfo newFallbackNetwork = + null != fallbackReassignment ? fallbackReassignment.mNewNetwork : null; + + if (oldFallbackNetwork != newFallbackNetwork) { // Maintain the illusion : since the legacy API only understands one network at a time, // if the default network changed, apps should see a disconnected broadcast for the // old default network before they see a connected broadcast for the new one. - if (oldDefaultNetwork != null) { - mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(), - oldDefaultNetwork, true); + if (oldFallbackNetwork != null) { + mLegacyTypeTracker.remove(oldFallbackNetwork.networkInfo.getType(), + oldFallbackNetwork, true); } - if (newDefaultNetwork != null) { + if (newFallbackNetwork != null) { // The new default network can be newly null if and only if the old default // network doesn't satisfy the default request any more because it lost a // capability. - mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0; - mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork); + mDefaultInetConditionPublished = newFallbackNetwork.lastValidated ? 100 : 0; + mLegacyTypeTracker.add( + newFallbackNetwork.networkInfo.getType(), newFallbackNetwork); // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast // to reflect the NetworkInfo of this new network. This broadcast has to be sent // after the disconnect broadcasts above, but before the broadcasts sent by the // legacy type tracker below. // TODO : refactor this, it's too complex - notifyLockdownVpn(newDefaultNetwork); + notifyLockdownVpn(newFallbackNetwork); } } @@ -7652,7 +7686,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above, - // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest + // because usually there are no NetworkRequests it satisfies (e.g., mFallbackRequest // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the // newNetwork to the tracker explicitly (it's a no-op if it has already been added). if (nai.isVPN()) { @@ -7663,9 +7697,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private void updateInetCondition(NetworkAgentInfo nai) { // Don't bother updating until we've graduated to validated at least once. if (!nai.everValidated) return; - // For now only update icons for default connection. + // For now only update icons for the fallback connection. // TODO: Update WiFi and cellular icons separately. b/17237507 - if (!isDefaultNetwork(nai)) return; + if (!isFallbackNetwork(nai)) return; int newInetCondition = nai.lastValidated ? 100 : 0; // Don't repeat publish. @@ -7933,8 +7967,8 @@ public class ConnectivityService extends IConnectivityManager.Stub intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); } NetworkAgentInfo newDefaultAgent = null; - if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { - newDefaultAgent = getDefaultNetwork(); + if (nai.isSatisfyingRequest(mFallbackRequest.mRequests.get(0).requestId)) { + newDefaultAgent = getFallbackNetwork(); if (newDefaultAgent != null) { intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, newDefaultAgent.networkInfo); @@ -7981,10 +8015,10 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private Network[] getDefaultNetworks() { ensureRunningOnConnectivityServiceThread(); - ArrayList<Network> defaultNetworks = new ArrayList<>(); - NetworkAgentInfo defaultNetwork = getDefaultNetwork(); + final ArrayList<Network> defaultNetworks = new ArrayList<>(); + final NetworkAgentInfo fallbackNetwork = getFallbackNetwork(); for (NetworkAgentInfo nai : mNetworkAgentInfos) { - if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) { + if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) { defaultNetworks.add(nai.network); } } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 8562b0d9cb82..6a72010738db 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -27,10 +27,12 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; +import android.net.TelephonyNetworkSpecifier; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnUnderlyingNetworkPolicy; +import android.net.wifi.WifiInfo; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -291,6 +293,12 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull VcnConfig config) { return new Vcn(vcnContext, subscriptionGroup, config); } + + /** Gets the subId indicated by the given {@link WifiInfo}. */ + public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) { + // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } } /** Notifies the VcnManagementService that external dependencies can be set up. */ @@ -582,8 +590,36 @@ public class VcnManagementService extends IVcnManagementService.Stub { "Must have permission NETWORK_FACTORY or be the SystemServer to get underlying" + " Network policies"); - // TODO(b/175914059): implement policy generation once VcnManagementService is able to - // determine policies + // Defensive copy in case this call is in-process and the given NetworkCapabilities mutates + networkCapabilities = new NetworkCapabilities(networkCapabilities); + + int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + && networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) { + TelephonyNetworkSpecifier telephonyNetworkSpecifier = + (TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier(); + subId = telephonyNetworkSpecifier.getSubscriptionId(); + } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + && networkCapabilities.getTransportInfo() instanceof WifiInfo) { + WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo(); + subId = mDeps.getSubIdForWifiInfo(wifiInfo); + } + + boolean isVcnManagedNetwork = false; + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + synchronized (mLock) { + ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId); + + // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode + if (mVcns.containsKey(subGroup)) { + isVcnManagedNetwork = true; + } + } + } + if (isVcnManagedNetwork) { + networkCapabilities.removeCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); + } return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); } diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index 6427ae2dc13c..fd12c2d2ebb8 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -18,16 +18,28 @@ package com.android.server.vcn; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.NetworkCapabilities.NetCapability; +import android.net.NetworkRequest; +import android.net.TelephonyNetworkSpecifier; import android.os.Handler; import android.os.ParcelUuid; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import java.util.List; import java.util.Objects; +import java.util.Set; /** * Tracks a set of Networks underpinning a VcnGatewayConnection. @@ -38,53 +50,385 @@ import java.util.Objects; * * @hide */ -public class UnderlyingNetworkTracker extends Handler { +public class UnderlyingNetworkTracker { @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName(); @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final UnderlyingNetworkTrackerCallback mCb; @NonNull private final Dependencies mDeps; + @NonNull private final Handler mHandler; + @NonNull private final ConnectivityManager mConnectivityManager; + @NonNull private final SubscriptionManager mSubscriptionManager; + + @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>(); + @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback(); + @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback(); + + @NonNull private final Set<Integer> mSubIds = new ArraySet<>(); + + @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities; + + @Nullable private UnderlyingNetworkRecord mCurrentRecord; + @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress; public UnderlyingNetworkTracker( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, + @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull UnderlyingNetworkTrackerCallback cb) { - this(vcnContext, subscriptionGroup, cb, new Dependencies()); + this( + vcnContext, + subscriptionGroup, + requiredUnderlyingNetworkCapabilities, + cb, + new Dependencies()); } private UnderlyingNetworkTracker( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, + @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull UnderlyingNetworkTrackerCallback cb, @NonNull Dependencies deps) { - super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); - mVcnContext = vcnContext; + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + mRequiredUnderlyingNetworkCapabilities = + Objects.requireNonNull( + requiredUnderlyingNetworkCapabilities, + "Missing requiredUnderlyingNetworkCapabilities"); mCb = Objects.requireNonNull(cb, "Missing cb"); mDeps = Objects.requireNonNull(deps, "Missing deps"); + + mHandler = new Handler(mVcnContext.getLooper()); + + mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); + mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class); + + registerNetworkRequests(); + } + + private void registerNetworkRequests() { + // register bringup requests for underlying Networks + mConnectivityManager.requestBackgroundNetwork( + getWifiNetworkRequest(), mHandler, mWifiBringupCallback); + updateSubIdsAndCellularRequests(); + + // register Network-selection request used to decide selected underlying Network + mConnectivityManager.requestBackgroundNetwork( + getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback); + } + + private NetworkRequest getWifiNetworkRequest() { + return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build(); + } + + private NetworkRequest getCellNetworkRequestForSubId(int subId) { + return getNetworkRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)) + .build(); + } + + private NetworkRequest.Builder getNetworkRequestBase() { + NetworkRequest.Builder requestBase = new NetworkRequest.Builder(); + for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) { + requestBase.addCapability(capability); + } + + return requestBase + .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); + } + + /** + * Update the current subIds and Cellular bringup requests for this UnderlyingNetworkTracker. + */ + private void updateSubIdsAndCellularRequests() { + mVcnContext.ensureRunningOnLooperThread(); + + Set<Integer> prevSubIds = new ArraySet<>(mSubIds); + mSubIds.clear(); + + // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup + // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony + List<SubscriptionInfo> subInfos = + mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup); + + for (SubscriptionInfo subInfo : subInfos) { + final int subId = subInfo.getSubscriptionId(); + mSubIds.add(subId); + + if (!mCellBringupCallbacks.contains(subId)) { + final NetworkBringupCallback cb = new NetworkBringupCallback(); + mCellBringupCallbacks.put(subId, cb); + + mConnectivityManager.requestBackgroundNetwork( + getCellNetworkRequestForSubId(subId), mHandler, cb); + } + } + + // unregister all NetworkCallbacks for outdated subIds + for (final int subId : prevSubIds) { + if (!mSubIds.contains(subId)) { + final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId); + mConnectivityManager.unregisterNetworkCallback(cb); + } + } } /** Tears down this Tracker, and releases all underlying network requests. */ - public void teardown() {} + public void teardown() { + mVcnContext.ensureRunningOnLooperThread(); + + mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback); + mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback); + + for (final int subId : mSubIds) { + final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId); + mConnectivityManager.unregisterNetworkCallback(cb); + } + mSubIds.clear(); + } + + /** Returns whether the currently selected Network matches the given network. */ + private static boolean isSameNetwork( + @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) { + return recordInProgress != null && recordInProgress.getNetwork().equals(network); + } + + /** Notify the Callback if a full UnderlyingNetworkRecord exists. */ + private void maybeNotifyCallback() { + // Only forward this update if a complete record has been received + if (!mRecordInProgress.isValid()) { + return; + } + + // Only forward this update if the updated record differs form the current record + UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build(); + if (!updatedRecord.equals(mCurrentRecord)) { + mCurrentRecord = updatedRecord; + + mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); + } + } - /** An record of a single underlying network, caching relevant fields. */ + private void handleNetworkAvailable(@NonNull Network network) { + mVcnContext.ensureRunningOnLooperThread(); + + mRecordInProgress = new UnderlyingNetworkRecord.Builder(network); + } + + private void handleNetworkLost(@NonNull Network network) { + mVcnContext.ensureRunningOnLooperThread(); + + if (!isSameNetwork(mRecordInProgress, network)) { + Slog.wtf(TAG, "Non-underlying Network lost"); + return; + } + + mRecordInProgress = null; + mCurrentRecord = null; + mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */); + } + + private void handleCapabilitiesChanged( + @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { + mVcnContext.ensureRunningOnLooperThread(); + + if (!isSameNetwork(mRecordInProgress, network)) { + Slog.wtf(TAG, "Invalid update to NetworkCapabilities"); + return; + } + + mRecordInProgress.setNetworkCapabilities(networkCapabilities); + + maybeNotifyCallback(); + } + + private void handleNetworkSuspended(@NonNull Network network, boolean isSuspended) { + mVcnContext.ensureRunningOnLooperThread(); + + if (!isSameNetwork(mRecordInProgress, network)) { + Slog.wtf(TAG, "Invalid update to isSuspended"); + return; + } + + final NetworkCapabilities newCaps = + new NetworkCapabilities(mRecordInProgress.getNetworkCapabilities()); + if (isSuspended) { + newCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); + } else { + newCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); + } + + handleCapabilitiesChanged(network, newCaps); + } + + private void handlePropertiesChanged( + @NonNull Network network, @NonNull LinkProperties linkProperties) { + mVcnContext.ensureRunningOnLooperThread(); + + if (!isSameNetwork(mRecordInProgress, network)) { + Slog.wtf(TAG, "Invalid update to LinkProperties"); + return; + } + + mRecordInProgress.setLinkProperties(linkProperties); + + maybeNotifyCallback(); + } + + private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) { + mVcnContext.ensureRunningOnLooperThread(); + + if (!isSameNetwork(mRecordInProgress, network)) { + Slog.wtf(TAG, "Invalid update to isBlocked"); + return; + } + + mRecordInProgress.setIsBlocked(isBlocked); + + maybeNotifyCallback(); + } + + /** + * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped. + * + * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being + * reaped, and no action is taken on any events firing. + */ + @VisibleForTesting + class NetworkBringupCallback extends NetworkCallback {} + + /** + * RouteSelectionCallback is used to select the "best" underlying Network. + * + * <p>The "best" network is determined by ConnectivityService, which is treated as a source of + * truth. + */ + @VisibleForTesting + class RouteSelectionCallback extends NetworkCallback { + @Override + public void onAvailable(@NonNull Network network) { + handleNetworkAvailable(network); + } + + @Override + public void onLost(@NonNull Network network) { + handleNetworkLost(network); + } + + @Override + public void onCapabilitiesChanged( + @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { + handleCapabilitiesChanged(network, networkCapabilities); + } + + @Override + public void onNetworkSuspended(@NonNull Network network) { + handleNetworkSuspended(network, true /* isSuspended */); + } + + @Override + public void onNetworkResumed(@NonNull Network network) { + handleNetworkSuspended(network, false /* isSuspended */); + } + + @Override + public void onLinkPropertiesChanged( + @NonNull Network network, @NonNull LinkProperties linkProperties) { + handlePropertiesChanged(network, linkProperties); + } + + @Override + public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { + handleNetworkBlocked(network, isBlocked); + } + } + + /** A record of a single underlying network, caching relevant fields. */ public static class UnderlyingNetworkRecord { @NonNull public final Network network; @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; - public final boolean blocked; + public final boolean isBlocked; @VisibleForTesting(visibility = Visibility.PRIVATE) UnderlyingNetworkRecord( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties, - boolean blocked) { + boolean isBlocked) { this.network = network; this.networkCapabilities = networkCapabilities; this.linkProperties = linkProperties; - this.blocked = blocked; + this.isBlocked = isBlocked; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UnderlyingNetworkRecord)) return false; + final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o; + + return network.equals(that.network) + && networkCapabilities.equals(that.networkCapabilities) + && linkProperties.equals(that.linkProperties) + && isBlocked == that.isBlocked; + } + + @Override + public int hashCode() { + return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); + } + + /** Builder to incrementally construct an UnderlyingNetworkRecord. */ + private static class Builder { + @NonNull private final Network mNetwork; + + @Nullable private NetworkCapabilities mNetworkCapabilities; + @Nullable private LinkProperties mLinkProperties; + boolean mIsBlocked; + boolean mWasIsBlockedSet; + + private Builder(@NonNull Network network) { + mNetwork = network; + } + + @NonNull + private Network getNetwork() { + return mNetwork; + } + + private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { + mNetworkCapabilities = networkCapabilities; + } + + @Nullable + private NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } + + private void setLinkProperties(@NonNull LinkProperties linkProperties) { + mLinkProperties = linkProperties; + } + + private void setIsBlocked(boolean isBlocked) { + mIsBlocked = isBlocked; + mWasIsBlockedSet = true; + } + + private boolean isValid() { + return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet; + } + + private UnderlyingNetworkRecord build() { + return new UnderlyingNetworkRecord( + mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); + } } } @@ -95,9 +439,10 @@ public class UnderlyingNetworkTracker extends Handler { * * <p>This callback does NOT signal a mobility event. * - * @param underlying The details of the new underlying network + * @param underlyingNetworkRecord The details of the new underlying network */ - void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying); + void onSelectedUnderlyingNetworkChanged( + @Nullable UnderlyingNetworkRecord underlyingNetworkRecord); } private static class Dependencies {} diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index dba59bdbee7d..7399e56b3a95 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -55,4 +55,15 @@ public class VcnContext { public VcnNetworkProvider getVcnNetworkProvider() { return mVcnNetworkProvider; } + + /** + * Verifies that the caller is running on the VcnContext Thread. + * + * @throwsIllegalStateException if the caller is not running on the VcnContext Thread. + */ + public void ensureRunningOnLooperThread() { + if (getLooper().getThread() != Thread.currentThread()) { + throw new IllegalStateException("Not running on VcnMgmtSvc thread"); + } + } } diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 3cfa00eb6079..39c96069f9c6 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -65,6 +65,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -476,7 +477,10 @@ public class VcnGatewayConnection extends StateMachine { mUnderlyingNetworkTracker = mDeps.newUnderlyingNetworkTracker( - mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback); + mVcnContext, + subscriptionGroup, + mConnectionConfig.getAllUnderlyingCapabilities(), + mUnderlyingNetworkTrackerCallback); mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); IpSecTunnelInterface iface; @@ -1134,8 +1138,10 @@ public class VcnGatewayConnection extends StateMachine { public UnderlyingNetworkTracker newUnderlyingNetworkTracker( VcnContext vcnContext, ParcelUuid subscriptionGroup, + Set<Integer> requiredUnderlyingNetworkCapabilities, UnderlyingNetworkTrackerCallback callback) { - return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback); + return new UnderlyingNetworkTracker( + vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback); } /** Builds a new IkeSession. */ diff --git a/services/smartspace/OWNERS b/services/smartspace/OWNERS new file mode 100644 index 000000000000..19ef9d774e6a --- /dev/null +++ b/services/smartspace/OWNERS @@ -0,0 +1,2 @@ +srazdan@google.com +alexmang@google.com
\ No newline at end of file diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java new file mode 100644 index 000000000000..10339a818205 --- /dev/null +++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2020 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.telecom; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class represents the quality report that bluetooth framework sends + * whenever there's a bad voice quality is detected from their side. + * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)} + * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT. + * Note that this report will be sent only during an active voice/voip call. + * @hide + */ +@SystemApi +public final class BluetoothCallQualityReport implements Parcelable { + + /** + * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report + */ + public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = + "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT"; + + /** + * Extra key sent with {@link Call#sendCallEvent(String, Bundle)} + */ + public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = + "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT"; + + private final long mSentTimestampMillis; + private final boolean mChoppyVoice; + private final int mRssiDbm; + private final int mSnrDb; + private final int mRetransmittedPacketsCount; + private final int mPacketsNotReceivedCount; + private final int mNegativeAcknowledgementCount; + + /** + * @return Time in milliseconds since the epoch. Designates when report was sent. + * Used to determine whether this report arrived too late to be useful. + */ + public @ElapsedRealtimeLong long getSentTimestampMillis() { + return mSentTimestampMillis; + } + + /** + * @return {@code true} if bluetooth hardware detects voice is choppy + */ + public boolean isChoppyVoice() { + return mChoppyVoice; + } + + /** + * @return Received Signal Strength Indication (RSSI) value in dBm. + * This value shall be an absolute received signal strength value. + */ + public @IntRange(from = -127, to = 20) int getRssiDbm() { + return mRssiDbm; + } + + /** + * @return Signal-to-Noise Ratio (SNR) value in dB. + * The controller shall provide the average SNR of all the channels currently used by the link. + */ + public int getSnrDb() { + return mSnrDb; + } + + /** + * @return The number of retransmissions since the last event. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getRetransmittedPacketsCount() { + return mRetransmittedPacketsCount; + } + + /** + * @return No RX count since the last event. + * The count increases when no packet is received at the scheduled time slot or the received + * packet is corrupted. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getPacketsNotReceivedCount() { + return mPacketsNotReceivedCount; + } + + /** + * @return NAK (Negative Acknowledge) count since the last event. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getNegativeAcknowledgementCount() { + return mNegativeAcknowledgementCount; + } + + // + // Parcelable implementation + // + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeLong(mSentTimestampMillis); + out.writeBoolean(mChoppyVoice); + out.writeInt(mRssiDbm); + out.writeInt(mSnrDb); + out.writeInt(mRetransmittedPacketsCount); + out.writeInt(mPacketsNotReceivedCount); + out.writeInt(mNegativeAcknowledgementCount); + } + + public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR = + new Creator<BluetoothCallQualityReport>() { + @Override + public BluetoothCallQualityReport createFromParcel(Parcel in) { + return new BluetoothCallQualityReport(in); + } + + @Override + public BluetoothCallQualityReport[] newArray(int size) { + return new BluetoothCallQualityReport[size]; + } + }; + + /** + * Builder class for {@link ConnectionRequest} + */ + public static final class Builder { + private long mSentTimestampMillis; + private boolean mChoppyVoice; + private int mRssiDbm; + private int mSnrDb; + private int mRetransmittedPacketsCount; + private int mPacketsNotReceivedCount; + private int mNegativeAcknowledgementCount; + + public Builder() { } + + /** + * Set the time when report was sent in milliseconds since the epoch. + * @param sentTimestampMillis + */ + public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) { + mSentTimestampMillis = sentTimestampMillis; + return this; + } + + /** + * Set if bluetooth hardware detects voice is choppy + * @param choppyVoice + */ + public @NonNull Builder setChoppyVoice(boolean choppyVoice) { + mChoppyVoice = choppyVoice; + return this; + } + + /** + * Set Received Signal Strength Indication (RSSI) value in dBm. + * @param rssiDbm + */ + public @NonNull Builder setRssiDbm(int rssiDbm) { + mRssiDbm = rssiDbm; + return this; + } + + /** + * Set Signal-to-Noise Ratio (SNR) value in dB. + * @param snrDb + */ + public @NonNull Builder setSnrDb(int snrDb) { + mSnrDb = snrDb; + return this; + } + + /** + * Set The number of retransmissions since the last event. + * @param retransmittedPacketsCount + */ + public @NonNull Builder setRetransmittedPacketsCount( + int retransmittedPacketsCount) { + mRetransmittedPacketsCount = retransmittedPacketsCount; + return this; + } + + /** + * Set No RX count since the last event. + * @param packetsNotReceivedCount + */ + public @NonNull Builder setPacketsNotReceivedCount( + int packetsNotReceivedCount) { + mPacketsNotReceivedCount = packetsNotReceivedCount; + return this; + } + + /** + * Set NAK (Negative Acknowledge) count since the last event. + * @param negativeAcknowledgementCount + */ + public @NonNull Builder setNegativeAcknowledgementCount( + int negativeAcknowledgementCount) { + mNegativeAcknowledgementCount = negativeAcknowledgementCount; + return this; + } + + /** + * Build the {@link BluetoothCallQualityReport} + * @return Result of the builder + */ + public @NonNull BluetoothCallQualityReport build() { + return new BluetoothCallQualityReport(this); + } + } + + private BluetoothCallQualityReport(Parcel in) { + mSentTimestampMillis = in.readLong(); + mChoppyVoice = in.readBoolean(); + mRssiDbm = in.readInt(); + mSnrDb = in.readInt(); + mRetransmittedPacketsCount = in.readInt(); + mPacketsNotReceivedCount = in.readInt(); + mNegativeAcknowledgementCount = in.readInt(); + } + + private BluetoothCallQualityReport(Builder builder) { + mSentTimestampMillis = builder.mSentTimestampMillis; + mChoppyVoice = builder.mChoppyVoice; + mRssiDbm = builder.mRssiDbm; + mSnrDb = builder.mSnrDb; + mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount; + mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount; + mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount; + } +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 3a9896a5a91d..a42e3642c0d0 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4124,6 +4124,13 @@ public class CarrierConfigManager { */ public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool"; + /** + * Indicates temporarily unmetered mobile data is supported by the carrier. + * @hide + */ + public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL = + "network_temp_not_metered_supported_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -4673,6 +4680,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); + sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false); sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); } diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java index eddbb1002f20..8762b6a712f2 100644 --- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -280,6 +280,12 @@ public final class SipDelegateImsConfiguration implements Parcelable { "sip_config_path_header_string"; /** + * The SIP User-Agent header value used by the IMS stack during IMS registration. + */ + public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = + "sip_config_sip_user_agent_header_string"; + + /** * SIP User part string in contact header */ public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = @@ -292,12 +298,20 @@ public final class SipDelegateImsConfiguration implements Parcelable { "sip_config_p_access_network_info_header_string"; /** - * SIP P-last-access-network-info header string + * The SIP P-last-access-network-info header value, populated for networks that require this + * information to be provided in outgoing SIP messages. */ public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; /** + * The Cellular-Network-Info header value (See 3GPP 24.229, section 7.2.15), populated for + * networks that require this information to be provided as part of outgoing SIP messages. + */ + public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = + "sip_config_cellular_network_info_header_string"; + + /** * SIP P-associated-uri header string */ public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = @@ -320,9 +334,11 @@ public final class SipDelegateImsConfiguration implements Parcelable { KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING, KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING, KEY_SIP_CONFIG_PATH_HEADER_STRING, + KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING, KEY_SIP_CONFIG_URI_USER_PART_STRING, KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING, KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING, KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING }) @Retention(RetentionPolicy.SOURCE) diff --git a/tests/net/Android.bp b/tests/net/Android.bp index f6a2846c9b3c..ffde68eab578 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -36,7 +36,7 @@ java_defaults { "libvndksupport", "libziparchive", "libz", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], } @@ -53,6 +53,7 @@ android_test { jarjar_rules: "jarjar-rules.txt", static_libs: [ "androidx.test.rules", + "bouncycastle-repackaged-unbundled", "FrameworksNetCommonTests", "frameworks-base-testutils", "frameworks-net-integration-testutils", diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java index 076e41d33a8d..1abd39a32bdf 100644 --- a/tests/net/java/android/net/Ikev2VpnProfileTest.java +++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java @@ -30,7 +30,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.net.VpnProfile; import com.android.net.module.util.ProxyUtils; -import com.android.org.bouncycastle.x509.X509V1CertificateGenerator; +import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator; import org.junit.Before; import org.junit.Test; diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 4c658e1be1e1..b0cc7f1361f6 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -132,6 +132,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -252,6 +253,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.net.module.util.ArrayTrackRecord; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.connectivity.ConnectivityConstants; import com.android.server.connectivity.MockableSystemProperties; @@ -906,28 +908,69 @@ public class ConnectivityServiceTest { } /** - * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove - * operations have been processed. Before ConnectivityService can add or remove any requests, - * the factory must be told to expect those operations by calling expectAddRequestsWithScores or - * expectRemoveRequests. + * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove + * operations have been processed and test for them. */ private static class MockNetworkFactory extends NetworkFactory { private final ConditionVariable mNetworkStartedCV = new ConditionVariable(); private final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); - // Used to expect that requests be removed or added on a separate thread, without sleeping. - // Callers can call either expectAddRequestsWithScores() or expectRemoveRequests() exactly - // once, then cause some other thread to add or remove requests, then call - // waitForRequests(). - // It is not possible to wait for both add and remove requests. When adding, the queue - // contains the expected score. When removing, the value is unused, all matters is the - // number of objects in the queue. - private final LinkedBlockingQueue<Integer> mExpectations; + static class RequestEntry { + @NonNull + public final NetworkRequest request; - // Whether we are currently expecting requests to be added or removed. Valid only if - // mExpectations is non-empty. - private boolean mExpectingAdditions; + RequestEntry(@NonNull final NetworkRequest request) { + this.request = request; + } + + static final class Add extends RequestEntry { + public final int factorySerialNumber; + + Add(@NonNull final NetworkRequest request, final int factorySerialNumber) { + super(request); + this.factorySerialNumber = factorySerialNumber; + } + } + + static final class Remove extends RequestEntry { + Remove(@NonNull final NetworkRequest request) { + super(request); + } + } + } + + // History of received requests adds and removes. + private final ArrayTrackRecord<RequestEntry>.ReadHead mRequestHistory = + new ArrayTrackRecord<RequestEntry>().newReadHead(); + + private static <T> T failIfNull(@Nullable final T obj, @Nullable final String message) { + if (null == obj) fail(null != message ? message : "Must not be null"); + return obj; + } + + + public RequestEntry.Add expectRequestAdd() { + return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS, + it -> it instanceof RequestEntry.Add), "Expected request add"); + } + + public void expectRequestAdds(final int count) { + for (int i = count; i > 0; --i) { + expectRequestAdd(); + } + } + + public RequestEntry.Remove expectRequestRemove() { + return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS, + it -> it instanceof RequestEntry.Remove), "Expected request remove"); + } + + public void expectRequestRemoves(final int count) { + for (int i = count; i > 0; --i) { + expectRequestRemove(); + } + } // Used to collect the networks requests managed by this factory. This is a duplicate of // the internal information stored in the NetworkFactory (which is private). @@ -936,7 +979,6 @@ public class ConnectivityServiceTest { public MockNetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { super(looper, context, logTag, filter); - mExpectations = new LinkedBlockingQueue<>(); } public int getMyRequestCount() { @@ -970,95 +1012,33 @@ public class ConnectivityServiceTest { @Override protected void handleAddRequest(NetworkRequest request, int score, int factorySerialNumber) { - synchronized (mExpectations) { - final Integer expectedScore = mExpectations.poll(); // null if the queue is empty - - assertNotNull("Added more requests than expected (" + request + " score : " - + score + ")", expectedScore); - // If we're expecting anything, we must be expecting additions. - if (!mExpectingAdditions) { - fail("Can't add requests while expecting requests to be removed"); - } - if (expectedScore != score) { - fail("Expected score was " + expectedScore + " but actual was " + score - + " in added request"); - } - - // Add the request. - mNetworkRequests.put(request.requestId, request); - super.handleAddRequest(request, score, factorySerialNumber); - mExpectations.notify(); - } + mNetworkRequests.put(request.requestId, request); + super.handleAddRequest(request, score, factorySerialNumber); + mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber)); } @Override protected void handleRemoveRequest(NetworkRequest request) { - synchronized (mExpectations) { - final Integer expectedScore = mExpectations.poll(); // null if the queue is empty + mNetworkRequests.remove(request.requestId); + super.handleRemoveRequest(request); + mRequestHistory.add(new RequestEntry.Remove(request)); + } - assertTrue("Removed more requests than expected", expectedScore != null); - // If we're expecting anything, we must be expecting removals. - if (mExpectingAdditions) { - fail("Can't remove requests while expecting requests to be added"); - } + public void assertRequestCountEquals(final int count) { + assertEquals(count, getMyRequestCount()); + } - // Remove the request. - mNetworkRequests.remove(request.requestId); - super.handleRemoveRequest(request); - mExpectations.notify(); - } + @Override + public void terminate() { + super.terminate(); + // Make sure there are no remaining requests unaccounted for. + assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true)); } // Trigger releasing the request as unfulfillable public void triggerUnfulfillable(NetworkRequest r) { super.releaseRequestAsUnfulfillableByAnyFactory(r); } - - private void assertNoExpectations() { - if (mExpectations.size() != 0) { - fail("Can't add expectation, " + mExpectations.size() + " already pending"); - } - } - - // Expects that requests with the specified scores will be added. - public void expectAddRequestsWithScores(final int... scores) { - assertNoExpectations(); - mExpectingAdditions = true; - for (int score : scores) { - mExpectations.add(score); - } - } - - // Expects that count requests will be removed. - public void expectRemoveRequests(final int count) { - assertNoExpectations(); - mExpectingAdditions = false; - for (int i = 0; i < count; ++i) { - mExpectations.add(0); // For removals the score is ignored so any value will do. - } - } - - // Waits for the expected request additions or removals to happen within a timeout. - public void waitForRequests() throws InterruptedException { - final long deadline = SystemClock.elapsedRealtime() + TIMEOUT_MS; - synchronized (mExpectations) { - while (mExpectations.size() > 0 && SystemClock.elapsedRealtime() < deadline) { - mExpectations.wait(deadline - SystemClock.elapsedRealtime()); - } - } - final long count = mExpectations.size(); - final String msg = count + " requests still not " + - (mExpectingAdditions ? "added" : "removed") + - " after " + TIMEOUT_MS + " ms"; - assertEquals(msg, 0, count); - } - - public SparseArray<NetworkRequest> waitForNetworkRequests(final int count) - throws InterruptedException { - waitForRequests(); - assertEquals(count, getMyRequestCount()); - return mNetworkRequests; - } } private Set<UidRange> uidRangesForUid(int uid) { @@ -2595,12 +2575,6 @@ public class ConnectivityServiceTest { callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); } - private int[] makeIntArray(final int size, final int value) { - final int[] array = new int[size]; - Arrays.fill(array, value); - return array; - } - private void tryNetworkFactoryRequests(int capability) throws Exception { // Verify NOT_RESTRICTED is set appropriately final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability) @@ -2622,9 +2596,9 @@ public class ConnectivityServiceTest { mServiceContext, "testFactory", filter); testFactory.setScoreFilter(40); ConditionVariable cv = testFactory.getNetworkStartedCV(); - testFactory.expectAddRequestsWithScores(0); testFactory.register(); - testFactory.waitForNetworkRequests(1); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); int expectedRequestCount = 1; NetworkCallback networkCallback = null; // For non-INTERNET capabilities we cannot rely on the default request being present, so @@ -2633,13 +2607,12 @@ public class ConnectivityServiceTest { assertFalse(testFactory.getMyStartRequested()); NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build(); networkCallback = new NetworkCallback(); - testFactory.expectAddRequestsWithScores(0); // New request mCm.requestNetwork(request, networkCallback); expectedRequestCount++; - testFactory.waitForNetworkRequests(expectedRequestCount); + testFactory.expectRequestAdd(); } waitFor(cv); - assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); + testFactory.assertRequestCountEquals(expectedRequestCount); assertTrue(testFactory.getMyStartRequested()); // Now bring in a higher scored network. @@ -2653,15 +2626,14 @@ public class ConnectivityServiceTest { // When testAgent connects, ConnectivityService will re-send us all current requests with // the new score. There are expectedRequestCount such requests, and we must wait for all of // them. - testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 50)); testAgent.connect(false); testAgent.addCapability(capability); waitFor(cv); - testFactory.waitForNetworkRequests(expectedRequestCount); + testFactory.expectRequestAdds(expectedRequestCount); + testFactory.assertRequestCountEquals(expectedRequestCount); assertFalse(testFactory.getMyStartRequested()); // Bring in a bunch of requests. - testFactory.expectAddRequestsWithScores(makeIntArray(10, 50)); assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); ConnectivityManager.NetworkCallback[] networkCallbacks = new ConnectivityManager.NetworkCallback[10]; @@ -2671,24 +2643,24 @@ public class ConnectivityServiceTest { builder.addCapability(capability); mCm.requestNetwork(builder.build(), networkCallbacks[i]); } - testFactory.waitForNetworkRequests(10 + expectedRequestCount); + testFactory.expectRequestAdds(10); + testFactory.assertRequestCountEquals(10 + expectedRequestCount); assertFalse(testFactory.getMyStartRequested()); // Remove the requests. - testFactory.expectRemoveRequests(10); for (int i = 0; i < networkCallbacks.length; i++) { mCm.unregisterNetworkCallback(networkCallbacks[i]); } - testFactory.waitForNetworkRequests(expectedRequestCount); + testFactory.expectRequestRemoves(10); + testFactory.assertRequestCountEquals(expectedRequestCount); assertFalse(testFactory.getMyStartRequested()); // Drop the higher scored network. cv = testFactory.getNetworkStartedCV(); - // With the default network disconnecting, the requests are sent with score 0 to factories. - testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 0)); testAgent.disconnect(); waitFor(cv); - testFactory.waitForNetworkRequests(expectedRequestCount); + testFactory.expectRequestAdds(expectedRequestCount); + testFactory.assertRequestCountEquals(expectedRequestCount); assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); assertTrue(testFactory.getMyStartRequested()); @@ -2731,9 +2703,8 @@ public class ConnectivityServiceTest { final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), mServiceContext, "testFactory", filter); // Register the factory and don't be surprised when the default request arrives. - testFactory.expectAddRequestsWithScores(0); testFactory.register(); - testFactory.waitForNetworkRequests(1); + testFactory.expectRequestAdd(); testFactory.setScoreFilter(42); testFactory.terminate(); @@ -3876,38 +3847,37 @@ public class ConnectivityServiceTest { testFactory.setScoreFilter(40); // Register the factory and expect it to start looking for a network. - testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet. testFactory.register(); try { - testFactory.waitForNetworkRequests(1); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); assertTrue(testFactory.getMyStartRequested()); // Bring up wifi. The factory stops looking for a network. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); // Score 60 - 40 penalty for not validated yet, then 60 when it validates - testFactory.expectAddRequestsWithScores(20, 60); mWiFiNetworkAgent.connect(true); - testFactory.waitForRequests(); + // Default request and mobile always on request + testFactory.expectRequestAdds(2); assertFalse(testFactory.getMyStartRequested()); - ContentResolver cr = mServiceContext.getContentResolver(); - // Turn on mobile data always on. The factory starts looking again. - testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0 setAlwaysOnNetworks(true); - testFactory.waitForNetworkRequests(2); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(2); + assertTrue(testFactory.getMyStartRequested()); // Bring up cell data and check that the factory stops looking. assertLength(1, mCm.getAllNetworks()); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - testFactory.waitForNetworkRequests(2); - assertFalse( - testFactory.getMyStartRequested()); // Because the cell network outscores us. + testFactory.expectRequestAdds(2); // Unvalidated and validated + testFactory.assertRequestCountEquals(2); + // The cell network outscores the factory filter, so start is not requested. + assertFalse(testFactory.getMyStartRequested()); // Check that cell data stays up. waitForIdle(); @@ -3915,9 +3885,8 @@ public class ConnectivityServiceTest { assertLength(2, mCm.getAllNetworks()); // Turn off mobile data always on and expect the request to disappear... - testFactory.expectRemoveRequests(1); setAlwaysOnNetworks(false); - testFactory.waitForNetworkRequests(1); + testFactory.expectRequestRemove(); // ... and cell data to be torn down. cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); @@ -4224,46 +4193,33 @@ public class ConnectivityServiceTest { testFactory.setScoreFilter(40); // Register the factory and expect it to receive the default request. - testFactory.expectAddRequestsWithScores(0); testFactory.register(); - SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1); - - assertEquals(1, requests.size()); // have 1 request at this point - int origRequestId = requests.valueAt(0).requestId; + testFactory.expectRequestAdd(); // Now file the test request and expect it. - testFactory.expectAddRequestsWithScores(0); mCm.requestNetwork(nr, networkCallback); - requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point + final NetworkRequest newRequest = testFactory.expectRequestAdd().request; - int newRequestId = 0; - for (int i = 0; i < requests.size(); ++i) { - if (requests.valueAt(i).requestId != origRequestId) { - newRequestId = requests.valueAt(i).requestId; - break; - } - } - - testFactory.expectRemoveRequests(1); if (preUnregister) { mCm.unregisterNetworkCallback(networkCallback); // Simulate the factory releasing the request as unfulfillable: no-op since // the callback has already been unregistered (but a test that no exceptions are // thrown). - testFactory.triggerUnfulfillable(requests.get(newRequestId)); + testFactory.triggerUnfulfillable(newRequest); } else { // Simulate the factory releasing the request as unfulfillable and expect onUnavailable! - testFactory.triggerUnfulfillable(requests.get(newRequestId)); + testFactory.triggerUnfulfillable(newRequest); networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null); - testFactory.waitForRequests(); // unregister network callback - a no-op (since already freed by the // on-unavailable), but should not fail or throw exceptions. mCm.unregisterNetworkCallback(networkCallback); } + testFactory.expectRequestRemove(); + testFactory.terminate(); handlerThread.quit(); } diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt index a10a3c81bc86..e590fb76f2ec 100644 --- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt @@ -55,7 +55,7 @@ class LegacyTypeTrackerTest { private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL) private val mMockService = mock(ConnectivityService::class.java).apply { - doReturn(false).`when`(this).isDefaultNetwork(any()) + doReturn(false).`when`(this).isFallbackNetwork(any()) } private val mTracker = LegacyTypeTracker(mMockService).apply { supportedTypes.forEach { @@ -126,11 +126,11 @@ class LegacyTypeTrackerTest { fun testBroadcastOnDisconnect() { val mobileNai1 = mock(NetworkAgentInfo::class.java) val mobileNai2 = mock(NetworkAgentInfo::class.java) - doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1) + doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai1) mTracker.add(TYPE_MOBILE, mobileNai1) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE) reset(mMockService) - doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2) + doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai2) mTracker.add(TYPE_MOBILE, mobileNai2) verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt()) mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index e26bf19488d0..e7d334ebd490 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -22,7 +22,6 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -30,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -39,16 +39,20 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; +import android.net.NetworkCapabilities.Transport; +import android.net.TelephonyNetworkSpecifier; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; import android.net.vcn.VcnUnderlyingNetworkPolicy; +import android.net.wifi.WifiInfo; import android.os.IBinder; import android.os.ParcelUuid; import android.os.PersistableBundle; @@ -253,7 +257,14 @@ public class VcnManagementServiceTest { verify(mConfigReadWriteHelper).readFromDisk(); } - private void triggerSubscriptionTrackerCallback(Set<ParcelUuid> activeSubscriptionGroups) { + private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( + Set<ParcelUuid> activeSubscriptionGroups) { + return triggerSubscriptionTrackerCbAndGetSnapshot( + activeSubscriptionGroups, Collections.emptyMap()); + } + + private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( + Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) { final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class); doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups(); @@ -267,8 +278,14 @@ public class VcnManagementServiceTest { argThat(val -> activeSubscriptionGroups.contains(val)), eq(TEST_PACKAGE_NAME)); + doAnswer(invocation -> { + return subIdToGroupMap.get(invocation.getArgument(0)); + }).when(snapshot).getGroupForSubId(anyInt()); + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); cb.onNewSnapshot(snapshot); + + return snapshot; } private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() { @@ -287,7 +304,7 @@ public class VcnManagementServiceTest { @Test public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception { - triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1)); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG)); } @@ -296,7 +313,7 @@ public class VcnManagementServiceTest { final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); - triggerSubscriptionTrackerCallback(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); // Verify teardown after delay mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); @@ -311,13 +328,13 @@ public class VcnManagementServiceTest { final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); // Simulate SIM unloaded - triggerSubscriptionTrackerCallback(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); // Simulate new SIM loaded right during teardown delay. mTestLooper.moveTimeForward( VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2); mTestLooper.dispatchAll(); - triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2)); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); // Verify that even after the full timeout duration, the VCN instance is not torn down mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); @@ -331,7 +348,7 @@ public class VcnManagementServiceTest { final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2); // Simulate SIM unloaded - triggerSubscriptionTrackerCallback(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new // vcnInstance. @@ -496,14 +513,73 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); } + private void setUpVcnSubscription(int subId, ParcelUuid subGroup) { + mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + + triggerSubscriptionTrackerCbAndGetSnapshot( + Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup)); + } + + private void verifyMergedNetworkCapabilities( + NetworkCapabilities mergedCapabilities, @Transport int transportType) { + assertTrue(mergedCapabilities.hasTransport(transportType)); + assertFalse( + mergedCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)); + } + @Test - public void testGetUnderlyingNetworkPolicy() throws Exception { + public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception { + setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + + NetworkCapabilities nc = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) + .build(); + + VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR); + } + + @Test + public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception { + setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + + WifiInfo wifiInfo = mock(WifiInfo.class); + when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo); + when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID); + NetworkCapabilities nc = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setTransportInfo(wifiInfo) + .build(); + + VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI); + } + + @Test + public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception { + NetworkCapabilities nc = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) + .build(); + VcnUnderlyingNetworkPolicy policy = - mVcnMgmtSvc.getUnderlyingNetworkPolicy( - new NetworkCapabilities(), new LinkProperties()); + mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); assertFalse(policy.isTeardownRequested()); - assertNotNull(policy.getMergedNetworkCapabilities()); + assertEquals(nc, policy.getMergedNetworkCapabilities()); } @Test(expected = SecurityException.class) diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java new file mode 100644 index 000000000000..48e068d14182 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2021 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.vcn; + +import static com.android.server.vcn.VcnTestUtils.setupSystemService; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.net.TelephonyNetworkSpecifier; +import android.os.ParcelUuid; +import android.os.test.TestLooper; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; + +import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback; +import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback; +import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; +import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +public class UnderlyingNetworkTrackerTest { + private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); + private static final int INITIAL_SUB_ID_1 = 1; + private static final int INITIAL_SUB_ID_2 = 2; + + private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .build(); + private static final NetworkCapabilities SUSPENDED_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder(INITIAL_NETWORK_CAPABILITIES) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + .build(); + private static final NetworkCapabilities UPDATED_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); + + private static final LinkProperties INITIAL_LINK_PROPERTIES = + getLinkPropertiesWithName("initial_iface"); + private static final LinkProperties UPDATED_LINK_PROPERTIES = + getLinkPropertiesWithName("updated_iface"); + + @Mock private Context mContext; + @Mock private VcnNetworkProvider mVcnNetworkProvider; + @Mock private ConnectivityManager mConnectivityManager; + @Mock private SubscriptionManager mSubscriptionManager; + @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb; + @Mock private Network mNetwork; + + @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor; + + private TestLooper mTestLooper; + private VcnContext mVcnContext; + private UnderlyingNetworkTracker mUnderlyingNetworkTracker; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTestLooper = new TestLooper(); + mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider)); + doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + + setupSystemService( + mContext, + mConnectivityManager, + Context.CONNECTIVITY_SERVICE, + ConnectivityManager.class); + setupSystemService( + mContext, + mSubscriptionManager, + Context.TELEPHONY_SUBSCRIPTION_SERVICE, + SubscriptionManager.class); + + List<SubscriptionInfo> initialSubInfos = + Arrays.asList( + getSubscriptionInfoForSubId(INITIAL_SUB_ID_1), + getSubscriptionInfoForSubId(INITIAL_SUB_ID_2)); + when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP))) + .thenReturn(initialSubInfos); + + mUnderlyingNetworkTracker = + new UnderlyingNetworkTracker( + mVcnContext, + SUB_GROUP, + Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), + mNetworkTrackerCb); + } + + private static LinkProperties getLinkPropertiesWithName(String iface) { + LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(iface); + return linkProperties; + } + + private SubscriptionInfo getSubscriptionInfoForSubId(int subId) { + SubscriptionInfo subInfo = mock(SubscriptionInfo.class); + when(subInfo.getSubscriptionId()).thenReturn(subId); + return subInfo; + } + + @Test + public void testNetworkCallbacksRegisteredOnStartup() { + // verify NetworkCallbacks registered when instantiated + verify(mConnectivityManager) + .requestBackgroundNetwork( + eq(getWifiRequest()), + any(), + any(NetworkBringupCallback.class)); + verify(mConnectivityManager) + .requestBackgroundNetwork( + eq(getCellRequestForSubId(INITIAL_SUB_ID_1)), + any(), + any(NetworkBringupCallback.class)); + verify(mConnectivityManager) + .requestBackgroundNetwork( + eq(getCellRequestForSubId(INITIAL_SUB_ID_2)), + any(), + any(NetworkBringupCallback.class)); + verify(mConnectivityManager) + .requestBackgroundNetwork( + eq(getRouteSelectionRequest()), + any(), + any(RouteSelectionCallback.class)); + + verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP)); + } + + private NetworkRequest getWifiRequest() { + return getExpectedRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build(); + } + + private NetworkRequest getCellRequestForSubId(int subId) { + return getExpectedRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)) + .build(); + } + + private NetworkRequest getRouteSelectionRequest() { + return getExpectedRequestBase().build(); + } + + private NetworkRequest.Builder getExpectedRequestBase() { + return new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); + } + + @Test + public void testTeardown() { + mUnderlyingNetworkTracker.teardown(); + + // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x + // for each subId) + verify(mConnectivityManager, times(3)) + .unregisterNetworkCallback(any(NetworkBringupCallback.class)); + verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class)); + } + + @Test + public void testUnderlyingNetworkRecordEquals() { + UnderlyingNetworkRecord recordA = + new UnderlyingNetworkRecord( + mNetwork, + INITIAL_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + UnderlyingNetworkRecord recordB = + new UnderlyingNetworkRecord( + mNetwork, + INITIAL_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + UnderlyingNetworkRecord recordC = + new UnderlyingNetworkRecord( + mNetwork, + UPDATED_NETWORK_CAPABILITIES, + UPDATED_LINK_PROPERTIES, + false /* isBlocked */); + + assertEquals(recordA, recordB); + assertNotEquals(recordA, recordC); + } + + @Test + public void testRecordTrackerCallbackNotifiedForNetworkChange() { + verifyRegistrationOnAvailableAndGetCallback(); + } + + private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() { + return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES); + } + + private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback( + NetworkCapabilities networkCapabilities) { + verify(mConnectivityManager) + .requestBackgroundNetwork( + eq(getRouteSelectionRequest()), + any(), + mRouteSelectionCallbackCaptor.capture()); + + RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue(); + cb.onAvailable(mNetwork); + cb.onCapabilitiesChanged(mNetwork, networkCapabilities); + cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES); + cb.onBlockedStatusChanged(mNetwork, false /* isFalse */); + + UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + networkCapabilities, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + return cb; + } + + @Test + public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() { + RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + + cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES); + + UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + UPDATED_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + } + + @Test + public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() { + RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + + cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES); + + UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + INITIAL_NETWORK_CAPABILITIES, + UPDATED_LINK_PROPERTIES, + false /* isBlocked */); + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + } + + @Test + public void testRecordTrackerCallbackNotifiedForNetworkSuspended() { + RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + + cb.onNetworkSuspended(mNetwork); + + UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + SUSPENDED_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + } + + @Test + public void testRecordTrackerCallbackNotifiedForNetworkResumed() { + RouteSelectionCallback cb = + verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES); + + cb.onNetworkResumed(mNetwork); + + UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + INITIAL_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + } + + @Test + public void testRecordTrackerCallbackNotifiedForBlocked() { + RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + + cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */); + + UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + INITIAL_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + true /* isBlocked */); + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + } + + @Test + public void testRecordTrackerCallbackNotifiedForNetworkLoss() { + RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + + cb.onLost(mNetwork); + + verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null); + } + + @Test + public void testRecordTrackerCallbackIgnoresDuplicateRecord() { + RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + + cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES); + + // Verify no more calls to the UnderlyingNetworkTrackerCallback when the + // UnderlyingNetworkRecord does not actually change + verifyNoMoreInteractions(mNetworkTrackerCb); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index b4d39bf74a4b..4d92fb9c42f2 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -94,7 +94,7 @@ public class VcnGatewayConnectionTestBase { doReturn(mUnderlyingNetworkTracker) .when(mDeps) - .newUnderlyingNetworkTracker(any(), any(), any()); + .newUnderlyingNetworkTracker(any(), any(), any(), any()); } @Before |