Merge "Remove 1.3s "Waiting for Debugger" delay"
diff --git a/core/api/current.txt b/core/api/current.txt
index 2ba2f5b..c9b5af5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16888,6 +16888,7 @@
method public int describeContents();
method public int getFormat();
method public int getHeight();
+ method public long getId();
method public int getLayers();
method public long getUsage();
method public int getWidth();
@@ -21303,7 +21304,7 @@
field public static final int ERROR_RECLAIMED = 1101; // 0x44d
}
- public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
+ public static final class MediaCodec.CryptoException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaCodec.CryptoException(int, @Nullable String);
method public int getErrorCode();
field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
@@ -21803,7 +21804,7 @@
method public void setMediaDrmSession(@NonNull byte[]) throws android.media.MediaCryptoException;
}
- public final class MediaCryptoException extends java.lang.Exception {
+ public final class MediaCryptoException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaCryptoException(@Nullable String);
}
@@ -22026,7 +22027,7 @@
method public long getTimestampMillis();
}
- public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
+ public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
method @NonNull public String getDiagnosticInfo();
method public int getErrorCode();
method public boolean isTransient();
@@ -22099,7 +22100,7 @@
@Deprecated @IntDef({android.media.MediaDrm.SECURITY_LEVEL_UNKNOWN, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.SecurityLevel {
}
- public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
+ public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
method public boolean isTransient();
@@ -22107,14 +22108,20 @@
field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0
}
- public class MediaDrmException extends java.lang.Exception {
+ public class MediaDrmException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaDrmException(String);
}
- public class MediaDrmResetException extends java.lang.IllegalStateException {
+ public class MediaDrmResetException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
ctor public MediaDrmResetException(String);
}
+ public interface MediaDrmThrowable {
+ method public default int getErrorContext();
+ method public default int getOemError();
+ method public default int getVendorError();
+ }
+
public final class MediaExtractor {
ctor public MediaExtractor();
method public boolean advance();
@@ -27264,8 +27271,14 @@
package android.net.vcn {
public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method public int getCbs();
+ method public int getDun();
+ method public int getIms();
+ method public int getInternet();
+ method public int getMms();
method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
method public int getOpportunistic();
+ method public int getRcs();
method public int getRoaming();
method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
}
@@ -27273,11 +27286,17 @@
public static final class VcnCellUnderlyingNetworkTemplate.Builder {
ctor public VcnCellUnderlyingNetworkTemplate.Builder();
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setCbs(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setDun(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setIms(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setInternet(int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMms(int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRcs(int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
}
@@ -27285,6 +27304,7 @@
public final class VcnConfig implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
+ method @NonNull public java.util.Set<java.lang.Integer> getRestrictedUnderlyingNetworkTransports();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnConfig> CREATOR;
}
@@ -27293,6 +27313,7 @@
ctor public VcnConfig.Builder(@NonNull android.content.Context);
method @NonNull public android.net.vcn.VcnConfig.Builder addGatewayConnectionConfig(@NonNull android.net.vcn.VcnGatewayConnectionConfig);
method @NonNull public android.net.vcn.VcnConfig build();
+ method @NonNull public android.net.vcn.VcnConfig.Builder setRestrictedUnderlyingNetworkTransports(@NonNull java.util.Set<java.lang.Integer>);
}
public final class VcnGatewayConnectionConfig {
@@ -27301,13 +27322,17 @@
method @IntRange(from=0x500) public int getMaxMtu();
method @NonNull public long[] getRetryIntervalsMillis();
method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
+ method public boolean hasGatewayOption(int);
+ field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
}
public static final class VcnGatewayConnectionConfig.Builder {
ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.ipsec.ike.IkeTunnelConnectionParams);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addGatewayOption(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeGatewayOption(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
@@ -37652,6 +37677,11 @@
ctor public AlreadyPersonalizedException(@NonNull String, @NonNull Throwable);
}
+ public final class AuthenticationKeyMetadata {
+ method @NonNull public java.time.Instant getExpirationDate();
+ method @IntRange(from=0) public int getUsageCount();
+ }
+
public class CipherSuiteNotSupportedException extends android.security.identity.IdentityCredentialException {
ctor public CipherSuiteNotSupportedException(@NonNull String);
ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -37682,6 +37712,7 @@
public abstract class CredentialDataResult {
method @Nullable public abstract byte[] getDeviceMac();
method @NonNull public abstract byte[] getDeviceNameSpaces();
+ method @Nullable public byte[] getDeviceSignature();
method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
method @NonNull public abstract byte[] getStaticAuthenticationData();
@@ -37718,13 +37749,15 @@
method @NonNull public byte[] delete(@NonNull byte[]);
method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
- method @NonNull public abstract int[] getAuthenticationDataUsageCount();
+ method @Deprecated @NonNull public abstract int[] getAuthenticationDataUsageCount();
+ method @NonNull public java.util.List<android.security.identity.AuthenticationKeyMetadata> getAuthenticationKeyMetadata();
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
method @NonNull public byte[] proveOwnership(@NonNull byte[]);
method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
method @Deprecated public void setAllowUsingExpiredKeys(boolean);
- method public abstract void setAvailableAuthenticationKeys(int, int);
+ method @Deprecated public abstract void setAvailableAuthenticationKeys(int, int);
+ method public void setAvailableAuthenticationKeys(@IntRange(from=0) int, @IntRange(from=1) int, @IntRange(from=0) long);
method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
@@ -41804,6 +41837,9 @@
field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
field public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; // 0x3
+ field public static final int EPDG_ADDRESS_IPV4_ONLY = 2; // 0x2
+ field public static final int EPDG_ADDRESS_IPV4_PREFERRED = 0; // 0x0
+ field public static final int EPDG_ADDRESS_IPV6_PREFERRED = 1; // 0x1
field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
field public static final int EPDG_ADDRESS_STATIC = 0; // 0x0
@@ -41818,6 +41854,7 @@
field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_ctr_key_size_int_array";
field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
+ field public static final String KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT = "iwlan.epdg_address_ip_type_preference_int";
field public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = "iwlan.epdg_address_priority_int_array";
field public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = "iwlan.epdg_authentication_method_int";
field public static final String KEY_EPDG_PCO_ID_IPV4_INT = "iwlan.epdg_pco_id_ipv4_int";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index ed2c8eb..8d53959 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -446,6 +446,10 @@
package android.telephony {
+ public class CarrierConfigManager {
+ field public static final String KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT = "min_udp_port_4500_nat_timeout_sec_int";
+ }
+
public abstract class CellSignalStrength {
method public static int getNumSignalStrengthLevels();
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index d2e7536..3921408 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -516,3 +516,8 @@
name: "framework-configinfrastructure",
sdk_version: "module_current",
}
+
+java_library {
+ name: "framework-configinfrastructure.stubs.module_lib",
+ sdk_version: "module_current",
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f494fa6..db1db91 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1019,9 +1019,10 @@
registerService(Context.APPWIDGET_SERVICE, AppWidgetManager.class,
new CachedServiceFetcher<AppWidgetManager>() {
@Override
- public AppWidgetManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.APPWIDGET_SERVICE);
- return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b));
+ public AppWidgetManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
+ return b == null ? null : new AppWidgetManager(ctx,
+ IAppWidgetService.Stub.asInterface(b));
}});
registerService(Context.MIDI_SERVICE, MidiManager.class,
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 92f7dee..cb6aa09 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -31,6 +31,7 @@
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.util.IntArray;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -43,6 +44,7 @@
* @hide
*/
public class TransactionExecutorHelper {
+ private static final String TAG = TransactionExecutorHelper.class.getSimpleName();
// A penalty applied to path with destruction when looking for the shortest one.
private static final int DESTRUCTION_PENALTY = 10;
@@ -162,6 +164,11 @@
if (finalStates == null || finalStates.length == 0) {
return UNDEFINED;
}
+ if (r == null) {
+ // Early return because the ActivityClientRecord hasn't been created or cannot be found.
+ Log.w(TAG, "ActivityClientRecord was null");
+ return UNDEFINED;
+ }
final int currentState = r.getLifecycleState();
int closestState = UNDEFINED;
diff --git a/core/java/android/hardware/HardwareBuffer.aidl b/core/java/android/hardware/HardwareBuffer.aidl
index 5bdd966..1333f0d 100644
--- a/core/java/android/hardware/HardwareBuffer.aidl
+++ b/core/java/android/hardware/HardwareBuffer.aidl
@@ -16,4 +16,4 @@
package android.hardware;
-parcelable HardwareBuffer;
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h";
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 1e4c9501..11892fe 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -188,9 +188,6 @@
public static HardwareBuffer create(
@IntRange(from = 1) int width, @IntRange(from = 1) int height,
@Format int format, @IntRange(from = 1) int layers, @Usage long usage) {
- if (!HardwareBuffer.isSupportedFormat(format)) {
- throw new IllegalArgumentException("Invalid pixel format " + format);
- }
if (width <= 0) {
throw new IllegalArgumentException("Invalid width " + width);
}
@@ -226,9 +223,6 @@
*/
public static boolean isSupported(@IntRange(from = 1) int width, @IntRange(from = 1) int height,
@Format int format, @IntRange(from = 1) int layers, @Usage long usage) {
- if (!HardwareBuffer.isSupportedFormat(format)) {
- throw new IllegalArgumentException("Invalid pixel format " + format);
- }
if (width <= 0) {
throw new IllegalArgumentException("Invalid width " + width);
}
@@ -286,10 +280,7 @@
* Returns the width of this buffer in pixels.
*/
public int getWidth() {
- if (isClosed()) {
- throw new IllegalStateException("This HardwareBuffer has been closed and its width "
- + "cannot be obtained.");
- }
+ checkClosed("width");
return nGetWidth(mNativeObject);
}
@@ -297,10 +288,7 @@
* Returns the height of this buffer in pixels.
*/
public int getHeight() {
- if (isClosed()) {
- throw new IllegalStateException("This HardwareBuffer has been closed and its height "
- + "cannot be obtained.");
- }
+ checkClosed("height");
return nGetHeight(mNativeObject);
}
@@ -309,10 +297,7 @@
*/
@Format
public int getFormat() {
- if (isClosed()) {
- throw new IllegalStateException("This HardwareBuffer has been closed and its format "
- + "cannot be obtained.");
- }
+ checkClosed("format");
return nGetFormat(mNativeObject);
}
@@ -320,10 +305,7 @@
* Returns the number of layers in this buffer.
*/
public int getLayers() {
- if (isClosed()) {
- throw new IllegalStateException("This HardwareBuffer has been closed and its layer "
- + "count cannot be obtained.");
- }
+ checkClosed("layer count");
return nGetLayers(mNativeObject);
}
@@ -331,14 +313,27 @@
* Returns the usage flags of the usage hints set on this buffer.
*/
public long getUsage() {
- if (isClosed()) {
- throw new IllegalStateException("This HardwareBuffer has been closed and its usage "
- + "cannot be obtained.");
- }
+ checkClosed("usage");
return nGetUsage(mNativeObject);
}
/**
+ * Returns the system-wide unique id for this buffer
+ *
+ */
+ public long getId() {
+ checkClosed("id");
+ return nGetId(mNativeObject);
+ }
+
+ private void checkClosed(String name) {
+ if (isClosed()) {
+ throw new IllegalStateException("This HardwareBuffer has been closed and its "
+ + name + " cannot be obtained.");
+ }
+ }
+
+ /**
* Destroys this buffer immediately. Calling this method frees up any
* underlying native resources. After calling this method, this buffer
* must not be used in any way.
@@ -407,36 +402,6 @@
}
};
- /**
- * Validates whether a particular format is supported by HardwareBuffer.
- *
- * @param format The format to validate.
- *
- * @return True if <code>format</code> is a supported format. false otherwise.
- * See {@link #create(int, int, int, int, long)}.
- */
- private static boolean isSupportedFormat(@Format int format) {
- switch(format) {
- case RGBA_8888:
- case RGBA_FP16:
- case RGBA_1010102:
- case RGBX_8888:
- case RGB_565:
- case RGB_888:
- case BLOB:
- case YCBCR_420_888:
- case D_16:
- case D_24:
- case DS_24UI8:
- case D_FP32:
- case DS_FP32UI8:
- case S_UI8:
- case YCBCR_P010:
- return true;
- }
- return false;
- }
-
private static native long nCreateHardwareBuffer(int width, int height, int format, int layers,
long usage);
private static native long nCreateFromGraphicBuffer(GraphicBuffer graphicBuffer);
@@ -457,4 +422,6 @@
long usage);
@CriticalNative
private static native long nEstimateSize(long nativeObject);
+ @CriticalNative
+ private static native long nGetId(long nativeObject);
}
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 8aff911..2816706 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -208,7 +208,7 @@
}
private int getBucketIndex(float ambientBrightness) {
- if (ambientBrightness < mBucketBoundaries[0]) {
+ if (ambientBrightness < mBucketBoundaries[0] || Float.isNaN(ambientBrightness)) {
return -1;
}
int low = 0;
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 2d1a3fe..38b3174 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -15,6 +15,12 @@
*/
package android.net.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString;
@@ -37,10 +43,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -64,6 +73,23 @@
private static final int DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA = MATCH_ANY;
private final int mOpportunisticMatchCriteria;
+ private static final String CAPABILITIES_MATCH_CRITERIA_KEY = "mCapabilitiesMatchCriteria";
+ @NonNull private final Map<Integer, Integer> mCapabilitiesMatchCriteria;
+
+ private static final Map<Integer, Integer> CAPABILITIES_MATCH_CRITERIA_DEFAULT;
+
+ static {
+ Map<Integer, Integer> capsMatchCriteria = new HashMap<>();
+ capsMatchCriteria.put(NET_CAPABILITY_CBS, MATCH_ANY);
+ capsMatchCriteria.put(NET_CAPABILITY_DUN, MATCH_ANY);
+ capsMatchCriteria.put(NET_CAPABILITY_IMS, MATCH_ANY);
+ capsMatchCriteria.put(NET_CAPABILITY_INTERNET, MATCH_REQUIRED);
+ capsMatchCriteria.put(NET_CAPABILITY_MMS, MATCH_ANY);
+ capsMatchCriteria.put(NET_CAPABILITY_RCS, MATCH_ANY);
+
+ CAPABILITIES_MATCH_CRITERIA_DEFAULT = Collections.unmodifiableMap(capsMatchCriteria);
+ }
+
private VcnCellUnderlyingNetworkTemplate(
int meteredMatchCriteria,
int minEntryUpstreamBandwidthKbps,
@@ -73,7 +99,8 @@
Set<String> allowedNetworkPlmnIds,
Set<Integer> allowedSpecificCarrierIds,
int roamingMatchCriteria,
- int opportunisticMatchCriteria) {
+ int opportunisticMatchCriteria,
+ Map<Integer, Integer> capabilitiesMatchCriteria) {
super(
NETWORK_PRIORITY_TYPE_CELL,
meteredMatchCriteria,
@@ -85,6 +112,7 @@
mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
mRoamingMatchCriteria = roamingMatchCriteria;
mOpportunisticMatchCriteria = opportunisticMatchCriteria;
+ mCapabilitiesMatchCriteria = new HashMap<>(capabilitiesMatchCriteria);
validate();
}
@@ -94,6 +122,7 @@
protected void validate() {
super.validate();
validatePlmnIds(mAllowedNetworkPlmnIds);
+ validateCapabilitiesMatchCriteria(mCapabilitiesMatchCriteria);
Objects.requireNonNull(mAllowedSpecificCarrierIds, "matchingCarrierIds is null");
validateMatchCriteria(mRoamingMatchCriteria, "mRoamingMatchCriteria");
validateMatchCriteria(mOpportunisticMatchCriteria, "mOpportunisticMatchCriteria");
@@ -113,6 +142,28 @@
}
}
+ private static void validateCapabilitiesMatchCriteria(
+ Map<Integer, Integer> capabilitiesMatchCriteria) {
+ Objects.requireNonNull(capabilitiesMatchCriteria, "capabilitiesMatchCriteria is null");
+
+ boolean requiredCapabilityFound = false;
+ for (Map.Entry<Integer, Integer> entry : capabilitiesMatchCriteria.entrySet()) {
+ final int capability = entry.getKey();
+ final int matchCriteria = entry.getValue();
+
+ Preconditions.checkArgument(
+ CAPABILITIES_MATCH_CRITERIA_DEFAULT.containsKey(capability),
+ "NetworkCapability " + capability + "out of range");
+ validateMatchCriteria(matchCriteria, "capability " + capability);
+
+ requiredCapabilityFound |= (matchCriteria == MATCH_REQUIRED);
+ }
+
+ if (!requiredCapabilityFound) {
+ throw new IllegalArgumentException("No required capabilities found");
+ }
+ }
+
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
@@ -146,6 +197,19 @@
PersistableBundleUtils.toList(
specificCarrierIdsBundle, INTEGER_DESERIALIZER));
+ final PersistableBundle capabilitiesMatchCriteriaBundle =
+ in.getPersistableBundle(CAPABILITIES_MATCH_CRITERIA_KEY);
+ final Map<Integer, Integer> capabilitiesMatchCriteria;
+ if (capabilitiesMatchCriteriaBundle == null) {
+ capabilitiesMatchCriteria = CAPABILITIES_MATCH_CRITERIA_DEFAULT;
+ } else {
+ capabilitiesMatchCriteria =
+ PersistableBundleUtils.toMap(
+ capabilitiesMatchCriteriaBundle,
+ INTEGER_DESERIALIZER,
+ INTEGER_DESERIALIZER);
+ }
+
final int roamingMatchCriteria = in.getInt(ROAMING_MATCH_KEY);
final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
@@ -158,7 +222,8 @@
allowedNetworkPlmnIds,
allowedSpecificCarrierIds,
roamingMatchCriteria,
- opportunisticMatchCriteria);
+ opportunisticMatchCriteria,
+ capabilitiesMatchCriteria);
}
/** @hide */
@@ -178,6 +243,12 @@
new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
+ final PersistableBundle capabilitiesMatchCriteriaBundle =
+ PersistableBundleUtils.fromMap(
+ mCapabilitiesMatchCriteria, INTEGER_SERIALIZER, INTEGER_SERIALIZER);
+ result.putPersistableBundle(
+ CAPABILITIES_MATCH_CRITERIA_KEY, capabilitiesMatchCriteriaBundle);
+
result.putInt(ROAMING_MATCH_KEY, mRoamingMatchCriteria);
result.putInt(OPPORTUNISTIC_MATCH_KEY, mOpportunisticMatchCriteria);
@@ -225,12 +296,75 @@
return mOpportunisticMatchCriteria;
}
+ /**
+ * Returns the matching criteria for CBS networks.
+ *
+ * @see Builder#setCbs(int)
+ */
+ @MatchCriteria
+ public int getCbs() {
+ return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_CBS);
+ }
+
+ /**
+ * Returns the matching criteria for DUN networks.
+ *
+ * @see Builder#setDun(int)
+ */
+ @MatchCriteria
+ public int getDun() {
+ return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_DUN);
+ }
+ /**
+ * Returns the matching criteria for IMS networks.
+ *
+ * @see Builder#setIms(int)
+ */
+ @MatchCriteria
+ public int getIms() {
+ return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_IMS);
+ }
+ /**
+ * Returns the matching criteria for INTERNET networks.
+ *
+ * @see Builder#setInternet(int)
+ */
+ @MatchCriteria
+ public int getInternet() {
+ return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_INTERNET);
+ }
+ /**
+ * Returns the matching criteria for MMS networks.
+ *
+ * @see Builder#setMms(int)
+ */
+ @MatchCriteria
+ public int getMms() {
+ return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_MMS);
+ }
+
+ /**
+ * Returns the matching criteria for RCS networks.
+ *
+ * @see Builder#setRcs(int)
+ */
+ @MatchCriteria
+ public int getRcs() {
+ return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_RCS);
+ }
+
+ /** @hide */
+ public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
+ return Collections.unmodifiableMap(new HashMap<>(mCapabilitiesMatchCriteria));
+ }
+
@Override
public int hashCode() {
return Objects.hash(
super.hashCode(),
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
+ mCapabilitiesMatchCriteria,
mRoamingMatchCriteria,
mOpportunisticMatchCriteria);
}
@@ -248,6 +382,7 @@
final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
&& Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
+ && Objects.equals(mCapabilitiesMatchCriteria, rhs.mCapabilitiesMatchCriteria)
&& mRoamingMatchCriteria == rhs.mRoamingMatchCriteria
&& mOpportunisticMatchCriteria == rhs.mOpportunisticMatchCriteria;
}
@@ -261,6 +396,7 @@
if (!mAllowedNetworkPlmnIds.isEmpty()) {
pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds);
}
+ pw.println("mCapabilitiesMatchCriteria: " + mCapabilitiesMatchCriteria);
if (mRoamingMatchCriteria != DEFAULT_ROAMING_MATCH_CRITERIA) {
pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
}
@@ -277,6 +413,7 @@
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
+ @NonNull private final Map<Integer, Integer> mCapabilitiesMatchCriteria = new HashMap<>();
private int mRoamingMatchCriteria = DEFAULT_ROAMING_MATCH_CRITERIA;
private int mOpportunisticMatchCriteria = DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA;
@@ -287,7 +424,9 @@
private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
/** Construct a Builder object. */
- public Builder() {}
+ public Builder() {
+ mCapabilitiesMatchCriteria.putAll(CAPABILITIES_MATCH_CRITERIA_DEFAULT);
+ }
/**
* Set the matching criteria for metered networks.
@@ -461,6 +600,126 @@
return this;
}
+ /**
+ * Sets the matching criteria for CBS networks.
+ *
+ * <p>A template where {@code setCbs(MATCH_REQUIRED)} is called will only match CBS networks
+ * (ones with NET_CAPABILITY_CBS). A template where {@code setCbs(MATCH_FORBIDDEN)} is
+ * called will only match networks that do not support CBS (ones without
+ * NET_CAPABILITY_CBS).
+ *
+ * @param matchCriteria the matching criteria for CBS networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_CBS
+ */
+ @NonNull
+ public Builder setCbs(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setCbs");
+
+ mCapabilitiesMatchCriteria.put(NET_CAPABILITY_CBS, matchCriteria);
+ return this;
+ }
+
+ /**
+ * Sets the matching criteria for DUN networks.
+ *
+ * <p>A template where {@code setDun(MATCH_REQUIRED)} is called will only match DUN networks
+ * (ones with NET_CAPABILITY_DUN). A template where {@code setDun(MATCH_FORBIDDEN)} is
+ * called will only match networks that do not support DUN (ones without
+ * NET_CAPABILITY_DUN).
+ *
+ * @param matchCriteria the matching criteria for DUN networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_DUN
+ */
+ @NonNull
+ public Builder setDun(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setDun");
+
+ mCapabilitiesMatchCriteria.put(NET_CAPABILITY_DUN, matchCriteria);
+ return this;
+ }
+
+ /**
+ * Sets the matching criteria for IMS networks.
+ *
+ * <p>A template where {@code setIms(MATCH_REQUIRED)} is called will only match IMS networks
+ * (ones with NET_CAPABILITY_IMS). A template where {@code setIms(MATCH_FORBIDDEN)} is
+ * called will only match networks that do not support IMS (ones without
+ * NET_CAPABILITY_IMS).
+ *
+ * @param matchCriteria the matching criteria for IMS networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_IMS
+ */
+ @NonNull
+ public Builder setIms(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setIms");
+
+ mCapabilitiesMatchCriteria.put(NET_CAPABILITY_IMS, matchCriteria);
+ return this;
+ }
+
+ /**
+ * Sets the matching criteria for INTERNET networks.
+ *
+ * <p>A template where {@code setInternet(MATCH_REQUIRED)} is called will only match
+ * INTERNET networks (ones with NET_CAPABILITY_INTERNET). A template where {@code
+ * setInternet(MATCH_FORBIDDEN)} is called will only match networks that do not support
+ * INTERNET (ones without NET_CAPABILITY_INTERNET).
+ *
+ * @param matchCriteria the matching criteria for INTERNET networks. Defaults to {@link
+ * #MATCH_REQUIRED}.
+ * @see NetworkCapabilities#NET_CAPABILITY_INTERNET
+ */
+ @NonNull
+ public Builder setInternet(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setInternet");
+
+ mCapabilitiesMatchCriteria.put(NET_CAPABILITY_INTERNET, matchCriteria);
+ return this;
+ }
+
+ /**
+ * Sets the matching criteria for MMS networks.
+ *
+ * <p>A template where {@code setMms(MATCH_REQUIRED)} is called will only match MMS networks
+ * (ones with NET_CAPABILITY_MMS). A template where {@code setMms(MATCH_FORBIDDEN)} is
+ * called will only match networks that do not support MMS (ones without
+ * NET_CAPABILITY_MMS).
+ *
+ * @param matchCriteria the matching criteria for MMS networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_MMS
+ */
+ @NonNull
+ public Builder setMms(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setMms");
+
+ mCapabilitiesMatchCriteria.put(NET_CAPABILITY_MMS, matchCriteria);
+ return this;
+ }
+
+ /**
+ * Sets the matching criteria for RCS networks.
+ *
+ * <p>A template where {@code setRcs(MATCH_REQUIRED)} is called will only match RCS networks
+ * (ones with NET_CAPABILITY_RCS). A template where {@code setRcs(MATCH_FORBIDDEN)} is
+ * called will only match networks that do not support RCS (ones without
+ * NET_CAPABILITY_RCS).
+ *
+ * @param matchCriteria the matching criteria for RCS networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_RCS
+ */
+ @NonNull
+ public Builder setRcs(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setRcs");
+
+ mCapabilitiesMatchCriteria.put(NET_CAPABILITY_RCS, matchCriteria);
+ return this;
+ }
+
/** Build the VcnCellUnderlyingNetworkTemplate. */
@NonNull
public VcnCellUnderlyingNetworkTemplate build() {
@@ -473,7 +732,8 @@
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
mRoamingMatchCriteria,
- mOpportunisticMatchCriteria);
+ mOpportunisticMatchCriteria,
+ mCapabilitiesMatchCriteria);
}
}
}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index fd3fe37..dcf0026 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -15,16 +15,23 @@
*/
package android.net.vcn;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
@@ -32,6 +39,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
@@ -46,22 +54,36 @@
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
+ private static final Set<Integer> ALLOWED_TRANSPORTS = new ArraySet<>();
+
+ static {
+ ALLOWED_TRANSPORTS.add(TRANSPORT_WIFI);
+ ALLOWED_TRANSPORTS.add(TRANSPORT_CELLULAR);
+ }
+
private static final String PACKAGE_NAME_KEY = "mPackageName";
@NonNull private final String mPackageName;
private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
@NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
+ private static final Set<Integer> RESTRICTED_TRANSPORTS_DEFAULT =
+ Collections.singleton(TRANSPORT_WIFI);
+ private static final String RESTRICTED_TRANSPORTS_KEY = "mRestrictedTransports";
+ @NonNull private final Set<Integer> mRestrictedTransports;
+
private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile";
private final boolean mIsTestModeProfile;
private VcnConfig(
@NonNull String packageName,
@NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs,
+ @NonNull Set<Integer> restrictedTransports,
boolean isTestModeProfile) {
mPackageName = packageName;
mGatewayConnectionConfigs =
Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
+ mRestrictedTransports = Collections.unmodifiableSet(new ArraySet<>(restrictedTransports));
mIsTestModeProfile = isTestModeProfile;
validate();
@@ -82,6 +104,20 @@
new ArraySet<>(
PersistableBundleUtils.toList(
gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+
+ final PersistableBundle restrictedTransportsBundle =
+ in.getPersistableBundle(RESTRICTED_TRANSPORTS_KEY);
+ if (restrictedTransportsBundle == null) {
+ // RESTRICTED_TRANSPORTS_KEY was added in U and does not exist in VcnConfigs created in
+ // older platforms
+ mRestrictedTransports = RESTRICTED_TRANSPORTS_DEFAULT;
+ } else {
+ mRestrictedTransports =
+ new ArraySet<Integer>(
+ PersistableBundleUtils.toList(
+ restrictedTransportsBundle, INTEGER_DESERIALIZER));
+ }
+
mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY);
validate();
@@ -91,6 +127,19 @@
Objects.requireNonNull(mPackageName, "packageName was null");
Preconditions.checkCollectionNotEmpty(
mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty");
+
+ final Iterator<Integer> iterator = mRestrictedTransports.iterator();
+ while (iterator.hasNext()) {
+ final int transport = iterator.next();
+ if (!ALLOWED_TRANSPORTS.contains(transport)) {
+ iterator.remove();
+ Log.w(
+ TAG,
+ "Found invalid transport "
+ + transport
+ + " which might be from a new version of VcnConfig");
+ }
+ }
}
/**
@@ -110,6 +159,16 @@
}
/**
+ * Retrieve the transports that will be restricted by the VCN.
+ *
+ * @see Builder#setRestrictedUnderlyingNetworkTransports(Set)
+ */
+ @NonNull
+ public Set<Integer> getRestrictedUnderlyingNetworkTransports() {
+ return Collections.unmodifiableSet(mRestrictedTransports);
+ }
+
+ /**
* Returns whether or not this VcnConfig is restricted to test networks.
*
* @hide
@@ -134,6 +193,12 @@
new ArrayList<>(mGatewayConnectionConfigs),
VcnGatewayConnectionConfig::toPersistableBundle);
result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+
+ final PersistableBundle restrictedTransportsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mRestrictedTransports), INTEGER_SERIALIZER);
+ result.putPersistableBundle(RESTRICTED_TRANSPORTS_KEY, restrictedTransportsBundle);
+
result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile);
return result;
@@ -141,7 +206,8 @@
@Override
public int hashCode() {
- return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
+ return Objects.hash(
+ mPackageName, mGatewayConnectionConfigs, mRestrictedTransports, mIsTestModeProfile);
}
@Override
@@ -153,6 +219,7 @@
final VcnConfig rhs = (VcnConfig) other;
return mPackageName.equals(rhs.mPackageName)
&& mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs)
+ && mRestrictedTransports.equals(rhs.mRestrictedTransports)
&& mIsTestModeProfile == rhs.mIsTestModeProfile;
}
@@ -189,12 +256,15 @@
@NonNull
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
+ @NonNull private final Set<Integer> mRestrictedTransports = new ArraySet<>();
+
private boolean mIsTestModeProfile = false;
public Builder(@NonNull Context context) {
Objects.requireNonNull(context, "context was null");
mPackageName = context.getOpPackageName();
+ mRestrictedTransports.addAll(RESTRICTED_TRANSPORTS_DEFAULT);
}
/**
@@ -225,6 +295,36 @@
return this;
}
+ private void validateRestrictedTransportsOrThrow(Set<Integer> restrictedTransports) {
+ Objects.requireNonNull(restrictedTransports, "transports was null");
+
+ for (int transport : restrictedTransports) {
+ if (!ALLOWED_TRANSPORTS.contains(transport)) {
+ throw new IllegalArgumentException("Invalid transport " + transport);
+ }
+ }
+ }
+
+ /**
+ * Sets transports that will be restricted by the VCN.
+ *
+ * @param transports transports that will be restricted by VCN. Networks that include any
+ * of the transports will be marked as restricted. Only {@link
+ * NetworkCapabilities#TRANSPORT_WIFI} and {@link
+ * NetworkCapabilities#TRANSPORT_CELLULAR} are allowed. {@link
+ * NetworkCapabilities#TRANSPORT_WIFI} is marked restricted by default.
+ * @return this {@link Builder} instance, for chaining
+ * @throws IllegalArgumentException if the input contains unsupported transport types.
+ */
+ @NonNull
+ public Builder setRestrictedUnderlyingNetworkTransports(@NonNull Set<Integer> transports) {
+ validateRestrictedTransportsOrThrow(transports);
+
+ mRestrictedTransports.clear();
+ mRestrictedTransports.addAll(transports);
+ return this;
+ }
+
/**
* Restricts this VcnConfig to matching with test networks (only).
*
@@ -248,7 +348,11 @@
*/
@NonNull
public VcnConfig build() {
- return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
+ return new VcnConfig(
+ mPackageName,
+ mGatewayConnectionConfigs,
+ mRestrictedTransports,
+ mIsTestModeProfile);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 2339656..4c9d150 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -130,6 +130,30 @@
})
public @interface VcnSupportedCapability {}
+ /**
+ * Perform mobility update to attempt recovery from suspected data stalls.
+ *
+ * <p>If set, the gateway connection will monitor the data stall detection of the VCN network.
+ * When there is a suspected data stall, the gateway connection will attempt recovery by
+ * performing a mobility update on the underlying IKE session.
+ */
+ public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"VCN_GATEWAY_OPTION_"},
+ value = {
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY,
+ })
+ public @interface VcnGatewayOption {}
+
+ private static final Set<Integer> ALLOWED_GATEWAY_OPTIONS = new ArraySet<>();
+
+ static {
+ ALLOWED_GATEWAY_OPTIONS.add(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY);
+ }
+
private static final int DEFAULT_MAX_MTU = 1500;
/**
@@ -201,6 +225,9 @@
private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
@NonNull private final long[] mRetryIntervalsMs;
+ private static final String GATEWAY_OPTIONS_KEY = "mGatewayOptions";
+ @NonNull private final Set<Integer> mGatewayOptions;
+
/** Builds a VcnGatewayConnectionConfig with the specified parameters. */
private VcnGatewayConnectionConfig(
@NonNull String gatewayConnectionName,
@@ -208,12 +235,14 @@
@NonNull Set<Integer> exposedCapabilities,
@NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
@NonNull long[] retryIntervalsMs,
- @IntRange(from = MIN_MTU_V6) int maxMtu) {
+ @IntRange(from = MIN_MTU_V6) int maxMtu,
+ @NonNull Set<Integer> gatewayOptions) {
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;
mExposedCapabilities = new TreeSet(exposedCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
+ mGatewayOptions = Collections.unmodifiableSet(new ArraySet(gatewayOptions));
mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
if (mUnderlyingNetworkTemplates.isEmpty()) {
@@ -256,6 +285,20 @@
VcnUnderlyingNetworkTemplate::fromPersistableBundle);
}
+ final PersistableBundle gatewayOptionsBundle = in.getPersistableBundle(GATEWAY_OPTIONS_KEY);
+
+ if (gatewayOptionsBundle == null) {
+ // GATEWAY_OPTIONS_KEY was added in Android U. Thus VcnGatewayConnectionConfig created
+ // on old platforms will not have this data and will be assigned with the default value
+ mGatewayOptions = Collections.emptySet();
+ } else {
+ mGatewayOptions =
+ new ArraySet<>(
+ PersistableBundleUtils.toList(
+ gatewayOptionsBundle,
+ PersistableBundleUtils.INTEGER_DESERIALIZER));
+ }
+
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -279,6 +322,10 @@
Preconditions.checkArgument(
mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)");
+
+ for (int option : mGatewayOptions) {
+ validateGatewayOption(option);
+ }
}
private static void checkValidCapability(int capability) {
@@ -315,6 +362,12 @@
}
}
+ private static void validateGatewayOption(int option) {
+ if (!ALLOWED_GATEWAY_OPTIONS.contains(option)) {
+ throw new IllegalArgumentException("Invalid vcn gateway option: " + option);
+ }
+ }
+
/**
* Returns the configured Gateway Connection name.
*
@@ -399,6 +452,19 @@
}
/**
+ * Checks if the given VCN gateway option is enabled.
+ *
+ * @param option the option to check.
+ * @throws IllegalArgumentException if the provided option is invalid.
+ * @see Builder#addGatewayOption(int)
+ * @see Builder#removeGatewayOption(int)
+ */
+ public boolean hasGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ return mGatewayOptions.contains(option);
+ }
+
+ /**
* Converts this config to a PersistableBundle.
*
* @hide
@@ -418,11 +484,16 @@
PersistableBundleUtils.fromList(
mUnderlyingNetworkTemplates,
VcnUnderlyingNetworkTemplate::toPersistableBundle);
+ final PersistableBundle gatewayOptionsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mGatewayOptions),
+ PersistableBundleUtils.INTEGER_SERIALIZER);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle);
+ result.putPersistableBundle(GATEWAY_OPTIONS_KEY, gatewayOptionsBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
@@ -437,7 +508,8 @@
mExposedCapabilities,
mUnderlyingNetworkTemplates,
Arrays.hashCode(mRetryIntervalsMs),
- mMaxMtu);
+ mMaxMtu,
+ mGatewayOptions);
}
@Override
@@ -452,7 +524,8 @@
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
&& mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
- && mMaxMtu == rhs.mMaxMtu;
+ && mMaxMtu == rhs.mMaxMtu
+ && mGatewayOptions.equals(rhs.mGatewayOptions);
}
/**
@@ -470,6 +543,8 @@
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
+ @NonNull private final Set<Integer> mGatewayOptions = new ArraySet<>();
+
// TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent.
// Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS
// when on Cell.
@@ -628,6 +703,34 @@
}
/**
+ * Enables the specified VCN gateway option.
+ *
+ * @param option the option to be enabled
+ * @return this {@link Builder} instance, for chaining
+ * @throws IllegalArgumentException if the provided option is invalid
+ */
+ @NonNull
+ public Builder addGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ mGatewayOptions.add(option);
+ return this;
+ }
+
+ /**
+ * Resets (disables) the specified VCN gateway option.
+ *
+ * @param option the option to be disabled
+ * @return this {@link Builder} instance, for chaining
+ * @throws IllegalArgumentException if the provided option is invalid
+ */
+ @NonNull
+ public Builder removeGatewayOption(@VcnGatewayOption int option) {
+ validateGatewayOption(option);
+ mGatewayOptions.remove(option);
+ return this;
+ }
+
+ /**
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
@@ -640,7 +743,8 @@
mExposedCapabilities,
mUnderlyingNetworkTemplates,
mRetryIntervalsMs,
- mMaxMtu);
+ mMaxMtu,
+ mGatewayOptions);
}
}
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 3a7aea5..70cf973 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -114,13 +114,28 @@
public static final String VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY =
"vcn_restricted_transports";
+ /**
+ * Key for maximum number of parallel SAs for tunnel aggregation
+ *
+ * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be
+ * aggregated over the various tunnels.
+ *
+ * <p>Defaults to 1, unless overridden by carrier config
+ *
+ * @hide
+ */
+ @NonNull
+ public static final String VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY =
+ "vcn_tunnel_aggregation_sa_count_max";
+
/** List of Carrier Config options to extract from Carrier Config bundles. @hide */
@NonNull
public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS =
new String[] {
VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
- VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY
+ VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
};
private static final Map<
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index f545c30..0c7f529 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -30,6 +30,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.tech.MifareClassic;
@@ -525,6 +526,66 @@
}
/**
+ * Helper to check if this device has FEATURE_NFC_BEAM, but without using
+ * a context.
+ * Equivalent to
+ * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_BEAM)
+ */
+ private static boolean hasBeamFeature() {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get package manager, assuming no Android Beam feature");
+ return false;
+ }
+ try {
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Package manager query failed, assuming no Android Beam feature", e);
+ return false;
+ }
+ }
+
+ /**
+ * Helper to check if this device has FEATURE_NFC, but without using
+ * a context.
+ * Equivalent to
+ * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
+ */
+ private static boolean hasNfcFeature() {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
+ return false;
+ }
+ try {
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
+ return false;
+ }
+ }
+
+ /**
+ * Helper to check if this device is NFC HCE capable, by checking for
+ * FEATURE_NFC_HOST_CARD_EMULATION and/or FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * but without using a context.
+ */
+ private static boolean hasNfcHceFeature() {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
+ return false;
+ }
+ try {
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
+ return false;
+ }
+ }
+
+ /**
* Return list of Secure Elements which support off host card emulation.
*
* @return List<String> containing secure elements on the device which supports
@@ -533,21 +594,23 @@
* @hide
*/
public @NonNull List<String> getSupportedOffHostSecureElements() {
- if (mContext == null) {
- throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
- + " getSupportedOffHostSecureElements APIs");
- }
List<String> offHostSE = new ArrayList<String>();
- PackageManager pm = mContext.getPackageManager();
+ IPackageManager pm = ActivityThread.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature");
return offHostSE;
}
- if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) {
- offHostSE.add("SIM");
- }
- if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) {
- offHostSE.add("eSE");
+ try {
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC, 0)) {
+ offHostSE.add("SIM");
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE, 0)) {
+ offHostSE.add("eSE");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Package manager query failed, assuming no off-host CE feature", e);
+ offHostSE.clear();
+ return offHostSE;
}
return offHostSE;
}
@@ -559,19 +622,10 @@
*/
@UnsupportedAppUsage
public static synchronized NfcAdapter getNfcAdapter(Context context) {
- if (context == null) {
- if (sNullContextNfcAdapter == null) {
- sNullContextNfcAdapter = new NfcAdapter(null);
- }
- return sNullContextNfcAdapter;
- }
if (!sIsInitialized) {
- PackageManager pm = context.getPackageManager();
- sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
- sHasBeamFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM);
- boolean hasHceFeature =
- pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
- || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
+ sHasNfcFeature = hasNfcFeature();
+ sHasBeamFeature = hasBeamFeature();
+ boolean hasHceFeature = hasNfcHceFeature();
/* is this device meant to have NFC */
if (!sHasNfcFeature && !hasHceFeature) {
Log.v(TAG, "this device does not have NFC support");
@@ -607,6 +661,12 @@
sIsInitialized = true;
}
+ if (context == null) {
+ if (sNullContextNfcAdapter == null) {
+ sNullContextNfcAdapter = new NfcAdapter(null);
+ }
+ return sNullContextNfcAdapter;
+ }
NfcAdapter adapter = sNfcAdapters.get(context);
if (adapter == null) {
adapter = new NfcAdapter(context);
@@ -617,12 +677,8 @@
/** get handle to NFC service interface */
private static INfcAdapter getServiceInterface() {
- if (!sHasNfcFeature) {
- /* NFC is not supported */
- return null;
- }
/* get a handle to NFC service */
- IBinder b = ServiceManager.waitForService("nfc");
+ IBinder b = ServiceManager.getService("nfc");
if (b == null) {
return null;
}
@@ -652,15 +708,6 @@
"context not associated with any application (using a mock context?)");
}
- synchronized (NfcAdapter.class) {
- if (!sIsInitialized) {
- PackageManager pm = context.getPackageManager();
- sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
- }
- if (!sHasNfcFeature) {
- return null;
- }
- }
if (getServiceInterface() == null) {
// NFC is not available
return null;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 6a42091..0b56d19 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -22,9 +22,11 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
+import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
@@ -156,13 +158,18 @@
throw new UnsupportedOperationException();
}
if (!sIsInitialized) {
- PackageManager pm = context.getPackageManager();
+ IPackageManager pm = ActivityThread.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get PackageManager");
throw new UnsupportedOperationException();
}
- if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
- Log.e(TAG, "This device does not support card emulation");
+ try {
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
+ Log.e(TAG, "This device does not support card emulation");
+ throw new UnsupportedOperationException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "PackageManager query failed.");
throw new UnsupportedOperationException();
}
sIsInitialized = true;
diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
index 48bbf5b6..3c92455 100644
--- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -17,8 +17,10 @@
package android.nfc.cardemulation;
import android.app.Activity;
+import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.nfc.INfcFCardEmulation;
import android.nfc.NfcAdapter;
@@ -68,13 +70,18 @@
throw new UnsupportedOperationException();
}
if (!sIsInitialized) {
- PackageManager pm = context.getPackageManager();
+ IPackageManager pm = ActivityThread.getPackageManager();
if (pm == null) {
Log.e(TAG, "Cannot get PackageManager");
throw new UnsupportedOperationException();
}
- if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
- Log.e(TAG, "This device does not support NFC-F card emulation");
+ try {
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0)) {
+ Log.e(TAG, "This device does not support NFC-F card emulation");
+ throw new UnsupportedOperationException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "PackageManager query failed.");
throw new UnsupportedOperationException();
}
sIsInitialized = true;
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 394927e..b210c46 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -292,8 +292,10 @@
* If the service is not running, servicemanager will attempt to start it, and this function
* will wait for it to be ready.
*
- * @return {@code null} if the service is not declared in the manifest, or if there are
- * permission problems, or if there are fatal errors.
+ * @throws SecurityException if the process does not have the permissions to check
+ * isDeclared() for the service.
+ * @return {@code null} if the service is not declared in the manifest, or if there
+ * are fatal errors.
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
diff --git a/core/java/android/os/ThreadLocalWorkSource.java b/core/java/android/os/ThreadLocalWorkSource.java
index 894b1cc4..e9adb20 100644
--- a/core/java/android/os/ThreadLocalWorkSource.java
+++ b/core/java/android/os/ThreadLocalWorkSource.java
@@ -39,8 +39,8 @@
*/
public final class ThreadLocalWorkSource {
public static final int UID_NONE = Message.UID_NONE;
- private static final ThreadLocal<Integer> sWorkSourceUid =
- ThreadLocal.withInitial(() -> UID_NONE);
+ private static final ThreadLocal<int []> sWorkSourceUid =
+ ThreadLocal.withInitial(() -> new int[] {UID_NONE});
/**
* Returns the UID to blame for the code currently executed on this thread.
@@ -50,7 +50,7 @@
* <p>It can also be set manually using {@link #setUid(int)}.
*/
public static int getUid() {
- return sWorkSourceUid.get();
+ return sWorkSourceUid.get()[0];
}
/**
@@ -65,7 +65,7 @@
*/
public static long setUid(int uid) {
final long token = getToken();
- sWorkSourceUid.set(uid);
+ sWorkSourceUid.get()[0] = uid;
return token;
}
@@ -73,7 +73,7 @@
* Restores the state using the provided token.
*/
public static void restore(long token) {
- sWorkSourceUid.set(parseUidFromToken(token));
+ sWorkSourceUid.get()[0] = parseUidFromToken(token);
}
/**
@@ -88,7 +88,7 @@
* </pre>
*
* @return a token that can be used to restore the state.
- **/
+ */
public static long clear() {
return setUid(UID_NONE);
}
@@ -98,7 +98,7 @@
}
private static long getToken() {
- return sWorkSourceUid.get();
+ return sWorkSourceUid.get()[0];
}
private ThreadLocalWorkSource() {
diff --git a/core/java/android/security/rkp/IRemoteProvisioning.aidl b/core/java/android/security/rkp/IRemoteProvisioning.aidl
index 23d8159..0efaa89 100644
--- a/core/java/android/security/rkp/IRemoteProvisioning.aidl
+++ b/core/java/android/security/rkp/IRemoteProvisioning.aidl
@@ -58,11 +58,4 @@
*
*/
void getRegistration(String irpcName, IGetRegistrationCallback callback);
-
- /**
- * Cancel any active {@link getRegistration} call associated with the given
- * callback. If no getRegistration call is currently active, this function is
- * a noop.
- */
- void cancelGetRegistration(IGetRegistrationCallback callback);
}
diff --git a/core/java/android/service/controls/OWNERS b/core/java/android/service/controls/OWNERS
new file mode 100644
index 0000000..4bb78c7
--- /dev/null
+++ b/core/java/android/service/controls/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 802726
+asc@google.com
+kozynski@google.com
+juliacr@google.com
\ No newline at end of file
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index a24c1f9..492c938 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -77,7 +77,7 @@
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
- inputChannel, mMessageQueue);
+ mInputChannel, mMessageQueue);
mCloseGuard.open("InputEventReceiver.dispose");
}
diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java
index 9035f3f..64f62c7 100644
--- a/core/java/android/view/InputEventSender.java
+++ b/core/java/android/view/InputEventSender.java
@@ -65,7 +65,7 @@
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this),
- inputChannel, mMessageQueue);
+ mInputChannel, mMessageQueue);
mCloseGuard.open("InputEventSender.dispose");
}
diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS
index 108c42c..26c59a6 100644
--- a/core/java/android/view/autofill/OWNERS
+++ b/core/java/android/view/autofill/OWNERS
@@ -1,7 +1,10 @@
# Bug component: 351486
augale@google.com
+haoranzhang@google.com
joannechung@google.com
markpun@google.com
lpeter@google.com
+simranjit@google.com
tymtsai@google.com
+yunicorn@google.com
diff --git a/core/java/com/android/internal/security/VerityUtils.java b/core/java/com/android/internal/security/VerityUtils.java
index 7f45c09..3ab11a8 100644
--- a/core/java/com/android/internal/security/VerityUtils.java
+++ b/core/java/com/android/internal/security/VerityUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.security;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Build;
import android.os.SystemProperties;
import android.system.Os;
@@ -41,6 +42,7 @@
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -77,17 +79,23 @@
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
}
- /** Enables fs-verity for the file with a PKCS#7 detached signature file. */
- public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath)
+ /** Enables fs-verity for the file with an optional PKCS#7 detached signature file. */
+ public static void setUpFsverity(@NonNull String filePath, @Nullable String signaturePath)
throws IOException {
- if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
- throw new SecurityException("Signature file is unexpectedly large: " + signaturePath);
+ byte[] rawSignature = null;
+ if (signaturePath != null) {
+ Path path = Paths.get(signaturePath);
+ if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new SecurityException("Signature file is unexpectedly large: "
+ + signaturePath);
+ }
+ rawSignature = Files.readAllBytes(path);
}
- setUpFsverity(filePath, Files.readAllBytes(Paths.get(signaturePath)));
+ setUpFsverity(filePath, rawSignature);
}
- /** Enables fs-verity for the file with a PKCS#7 detached signature bytes. */
- public static void setUpFsverity(@NonNull String filePath, @NonNull byte[] pkcs7Signature)
+ /** Enables fs-verity for the file with an optional PKCS#7 detached signature bytes. */
+ public static void setUpFsverity(@NonNull String filePath, @Nullable byte[] pkcs7Signature)
throws IOException {
// This will fail if the public key is not already in .fs-verity kernel keyring.
int errno = enableFsverityNative(filePath, pkcs7Signature);
@@ -227,7 +235,7 @@
}
private static native int enableFsverityNative(@NonNull String filePath,
- @NonNull byte[] pkcs7Signature);
+ @Nullable byte[] pkcs7Signature);
private static native int measureFsverityNative(@NonNull String filePath,
@NonNull byte[] digest);
private static native int statxForFsverityNative(@NonNull String filePath);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 949f363..7dd46a6 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -640,7 +640,6 @@
char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];
char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
- char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX];
char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];
char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX];
char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
@@ -856,10 +855,7 @@
parseRuntimeOption("dalvik.vm.jitpthreadpriority",
jitpthreadpriorityOptsBuf,
"-Xjitpthreadpriority:");
- property_get("dalvik.vm.usejitprofiles", useJitProfilesOptsBuf, "");
- if (strcmp(useJitProfilesOptsBuf, "true") == 0) {
- addOption("-Xjitsaveprofilinginfo");
- }
+ addOption("-Xjitsaveprofilinginfo");
parseRuntimeOption("dalvik.vm.jitprithreadweight",
jitprithreadweightOptBuf,
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index f462523..5fcc46e 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -163,7 +163,7 @@
static jlong android_hardware_HardwareBuffer_getUsage(JNIEnv* env,
jobject clazz, jlong nativeObject) {
GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
- return AHardwareBuffer_convertFromGrallocUsageBits(buffer->getUsage());
+ return static_cast<jlong>(AHardwareBuffer_convertFromGrallocUsageBits(buffer->getUsage()));
}
static jlong android_hardware_HardwareBuffer_estimateSize(jlong nativeObject) {
@@ -177,7 +177,12 @@
const uint32_t bufferStride =
buffer->getStride() > 0 ? buffer->getStride() : buffer->getWidth();
- return static_cast<jlong>(buffer->getHeight() * bufferStride * bpp);
+ return static_cast<jlong>(static_cast<uint64_t>(buffer->getHeight() * bufferStride * bpp));
+}
+
+static jlong android_hardware_HardwareBuffer_getId(jlong nativeObject) {
+ GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+ return static_cast<jlong>(buffer->getId());
}
// ----------------------------------------------------------------------------
@@ -223,16 +228,6 @@
}
}
-GraphicBuffer* android_hardware_HardwareBuffer_getNativeGraphicBuffer(
- JNIEnv* env, jobject hardwareBufferObj) {
- if (env->IsInstanceOf(hardwareBufferObj, gHardwareBufferClassInfo.clazz)) {
- return GraphicBufferWrapper_to_GraphicBuffer(
- env->GetLongField(hardwareBufferObj, gHardwareBufferClassInfo.mNativeObject));
- } else {
- return nullptr;
- }
-}
-
jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
JNIEnv* env, AHardwareBuffer* hardwareBuffer) {
GraphicBuffer* buffer = AHardwareBuffer_to_GraphicBuffer(hardwareBuffer);
@@ -295,6 +290,7 @@
// --------------- @CriticalNative ----------------------
{ "nEstimateSize", "(J)J", (void*) android_hardware_HardwareBuffer_estimateSize },
+ { "nGetId", "(J)J", (void*) android_hardware_HardwareBuffer_getId },
};
// clang-format on
diff --git a/core/jni/com_android_internal_security_VerityUtils.cpp b/core/jni/com_android_internal_security_VerityUtils.cpp
index c5b3d8a..5553d28 100644
--- a/core/jni/com_android_internal_security_VerityUtils.cpp
+++ b/core/jni/com_android_internal_security_VerityUtils.cpp
@@ -48,10 +48,6 @@
if (rfd.get() < 0) {
return errno;
}
- ScopedByteArrayRO signature_bytes(env, signature);
- if (signature_bytes.get() == nullptr) {
- return EINVAL;
- }
fsverity_enable_arg arg = {};
arg.version = 1;
@@ -59,8 +55,18 @@
arg.block_size = 4096;
arg.salt_size = 0;
arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
- arg.sig_size = signature_bytes.size();
- arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
+
+ if (signature != nullptr) {
+ ScopedByteArrayRO signature_bytes(env, signature);
+ if (signature_bytes.get() == nullptr) {
+ return EINVAL;
+ }
+ arg.sig_size = signature_bytes.size();
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
+ } else {
+ arg.sig_size = 0;
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(nullptr);
+ }
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
return errno;
diff --git a/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h b/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h
index dfd8035..964c28f 100644
--- a/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h
+++ b/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h
@@ -28,10 +28,6 @@
extern AHardwareBuffer* android_hardware_HardwareBuffer_getNativeHardwareBuffer(
JNIEnv* env, jobject hardwareBufferObj);
-/* Gets the underlying GraphicBuffer for a HardwareBuffer. */
-extern GraphicBuffer* android_hardware_HardwareBuffer_getNativeGraphicBuffer(
- JNIEnv* env, jobject hardwareBufferObj);
-
/* Returns a HardwareBuffer wrapper for the underlying AHardwareBuffer. */
extern jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
JNIEnv* env, AHardwareBuffer* hardwareBuffer);
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index a4463e4..db391f7 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,8 @@
per-file package_item_info.proto = toddke@google.com,patb@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
+per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com
+per-file background_install_control.proto = wenhaowang@google.com,georgechan@google.com,billylau@google.com
# Biometrics
jaggies@google.com
diff --git a/core/res/OWNERS b/core/res/OWNERS
index a2ef400..b878189 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -21,6 +21,11 @@
tsuji@google.com
yamasani@google.com
+# WindowManager team
+# TODO(262451702): Move WindowManager configs out of config.xml in a separate file
+per-file core/res/res/values/config.xml = file:/services/core/java/com/android/server/wm/OWNERS
+per-file core/res/res/values/symbols.xml = file:/services/core/java/com/android/server/wm/OWNERS
+
# Resources finalization
per-file res/xml/public-staging.xml = file:/tools/aapt2/OWNERS
per-file res/xml/public-final.xml = file:/tools/aapt2/OWNERS
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 1327d96..ea2b988 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -17,12 +17,6 @@
<resources>
<!-- This file defines Android telephony related resources -->
- <!-- Whether force disabling telephony new data stack or not.
- This flag and the old data stack code will be deleted in Android 14.
- -->
- <bool name="config_force_disable_telephony_new_data_stack">false</bool>
- <java-symbol type="bool" name="config_force_disable_telephony_new_data_stack" />
-
<!-- Configure tcp buffer sizes per network type in the form:
network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
diff --git a/core/tests/coretests/src/android/companion/virtual/OWNERS b/core/tests/coretests/src/android/companion/virtual/OWNERS
new file mode 100644
index 0000000..1a3e927
--- /dev/null
+++ b/core/tests/coretests/src/android/companion/virtual/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /services/companion/java/com/android/server/companion/virtual/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/service/controls/OWNERS b/core/tests/coretests/src/android/service/controls/OWNERS
new file mode 100644
index 0000000..bf67034
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/service/controls/OWNERS
\ No newline at end of file
diff --git a/identity/java/android/security/identity/AuthenticationKeyMetadata.java b/identity/java/android/security/identity/AuthenticationKeyMetadata.java
new file mode 100644
index 0000000..c6abc22
--- /dev/null
+++ b/identity/java/android/security/identity/AuthenticationKeyMetadata.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 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.security.identity;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.time.Instant;
+
+/**
+ * Data about authentication keys.
+ */
+public final class AuthenticationKeyMetadata {
+ private int mUsageCount;
+ private Instant mExpirationDate;
+
+ AuthenticationKeyMetadata(int usageCount, @NonNull Instant expirationDate) {
+ mUsageCount = usageCount;
+ mExpirationDate = expirationDate;
+ }
+
+ /**
+ * Gets usage count for the authentication key.
+ *
+ * @return the usage count
+ */
+ public @IntRange(from = 0) int getUsageCount() {
+ return mUsageCount;
+ }
+
+ /**
+ * Gets expiration date for the authentication key.
+ *
+ * @return the expiration date of the authentication key.
+ */
+ public @NonNull Instant getExpirationDate() {
+ return mExpirationDate;
+ }
+}
diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java
index beb03af..dca039a 100644
--- a/identity/java/android/security/identity/CredentialDataResult.java
+++ b/identity/java/android/security/identity/CredentialDataResult.java
@@ -106,6 +106,30 @@
public abstract @Nullable byte[] getDeviceMac();
/**
+ * Returns a signature over the {@code DeviceAuthenticationBytes} CBOR
+ * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
+ * is from a trusted credential.
+ *
+ * <p>The signature is made using the authentication private key. See section 9.1.3.4 of
+ * ISO/IEC 18013-5:2021 for details of this operation.
+ *
+ * <p>If the session transcript or reader ephemeral key wasn't set on the {@link
+ * PresentationSession} used to obtain this data no signature will be produced and this method
+ * will return {@code null}.
+ *
+ * <p>This is only implemented in feature version 202301 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @return A COSE_Sign1 structure as described above or {@code null} if the conditions
+ * specified above are not met.
+ */
+ public @Nullable byte[] getDeviceSignature() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Returns the static authentication data associated with the dynamic authentication
* key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
*
diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
index 7afe3d4..b4fd5d3 100644
--- a/identity/java/android/security/identity/CredstoreCredentialDataResult.java
+++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
@@ -47,6 +47,11 @@
}
@Override
+ public @Nullable byte[] getDeviceSignature() {
+ return mDeviceSignedResult.getSignature();
+ }
+
+ @Override
public @NonNull byte[] getStaticAuthenticationData() {
return mDeviceSignedResult.getStaticAuthenticationData();
}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 8e01105..449c7a7 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -38,8 +38,9 @@
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import javax.crypto.BadPaddingException;
@@ -59,16 +60,19 @@
private Context mContext;
private ICredential mBinder;
private CredstorePresentationSession mSession;
+ private int mFeatureVersion;
CredstoreIdentityCredential(Context context, String credentialName,
@IdentityCredentialStore.Ciphersuite int cipherSuite,
ICredential binder,
- @Nullable CredstorePresentationSession session) {
+ @Nullable CredstorePresentationSession session,
+ int featureVersion) {
mContext = context;
mCredentialName = credentialName;
mCipherSuite = cipherSuite;
mBinder = binder;
mSession = session;
+ mFeatureVersion = featureVersion;
}
private KeyPair mEphemeralKeyPair = null;
@@ -227,7 +231,7 @@
throw new RuntimeException("Error decoding certificates", e);
}
- LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+ ArrayList<X509Certificate> x509Certs = new ArrayList<>();
for (Certificate cert : certs) {
x509Certs.add((X509Certificate) cert);
}
@@ -346,12 +350,18 @@
}
}
+ byte[] signature = resultParcel.signature;
+ if (signature != null && signature.length == 0) {
+ signature = null;
+ }
+
byte[] mac = resultParcel.mac;
if (mac != null && mac.length == 0) {
mac = null;
}
CredstoreResultData.Builder resultDataBuilder = new CredstoreResultData.Builder(
- resultParcel.staticAuthenticationData, resultParcel.deviceNameSpaces, mac);
+ mFeatureVersion, resultParcel.staticAuthenticationData,
+ resultParcel.deviceNameSpaces, mac, signature);
for (ResultNamespaceParcel resultNamespaceParcel : resultParcel.resultNamespaces) {
for (ResultEntryParcel resultEntryParcel : resultNamespaceParcel.entries) {
@@ -370,8 +380,14 @@
@Override
public void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+ setAvailableAuthenticationKeys(keyCount, maxUsesPerKey, 0);
+ }
+
+ @Override
+ public void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey,
+ long minValidTimeMillis) {
try {
- mBinder.setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+ mBinder.setAvailableAuthenticationKeys(keyCount, maxUsesPerKey, minValidTimeMillis);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -384,7 +400,7 @@
public @NonNull Collection<X509Certificate> getAuthKeysNeedingCertification() {
try {
AuthKeyParcel[] authKeyParcels = mBinder.getAuthKeysNeedingCertification();
- LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+ ArrayList<X509Certificate> x509Certs = new ArrayList<>();
CertificateFactory factory = CertificateFactory.getInstance("X.509");
for (AuthKeyParcel authKeyParcel : authKeyParcels) {
Collection<? extends Certificate> certs = null;
@@ -471,6 +487,34 @@
}
@Override
+ public @NonNull List<AuthenticationKeyMetadata> getAuthenticationKeyMetadata() {
+ try {
+ int[] usageCount = mBinder.getAuthenticationDataUsageCount();
+ long[] expirationsMillis = mBinder.getAuthenticationDataExpirations();
+ if (usageCount.length != expirationsMillis.length) {
+ throw new IllegalStateException("Size og usageCount and expirationMillis differ");
+ }
+ List<AuthenticationKeyMetadata> mds = new ArrayList<>();
+ for (int n = 0; n < expirationsMillis.length; n++) {
+ AuthenticationKeyMetadata md = null;
+ long expirationMillis = expirationsMillis[n];
+ if (expirationMillis != Long.MAX_VALUE) {
+ md = new AuthenticationKeyMetadata(
+ usageCount[n],
+ Instant.ofEpochMilli(expirationMillis));
+ }
+ mds.add(md);
+ }
+ return mds;
+ } catch (android.os.RemoteException e) {
+ throw new IllegalStateException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new IllegalStateException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
try {
byte[] proofOfOwnership = mBinder.proveOwnership(challenge);
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index bbaf086..d785c3c 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.GenerateRkpKey;
@@ -30,10 +32,28 @@
private Context mContext = null;
private ICredentialStore mStore = null;
+ private int mFeatureVersion;
+
+ static int getFeatureVersion(@NonNull Context context) {
+ PackageManager pm = context.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) {
+ FeatureInfo[] infos = pm.getSystemAvailableFeatures();
+ for (int n = 0; n < infos.length; n++) {
+ FeatureInfo info = infos[n];
+ if (info.name.equals(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) {
+ return info.version;
+ }
+ }
+ }
+ // Use of the system feature is not required since Android 12. So for Android 11
+ // return 202009 which is the feature version shipped with Android 11.
+ return 202009;
+ }
private CredstoreIdentityCredentialStore(@NonNull Context context, ICredentialStore store) {
mContext = context;
mStore = store;
+ mFeatureVersion = getFeatureVersion(mContext);
}
static CredstoreIdentityCredentialStore getInstanceForType(@NonNull Context context,
@@ -139,8 +159,7 @@
ICredential credstoreCredential;
credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
- credstoreCredential,
- null);
+ credstoreCredential, null, mFeatureVersion);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -182,7 +201,8 @@
throws CipherSuiteNotSupportedException {
try {
ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
- return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
+ return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession,
+ mFeatureVersion);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java
index e3c6689..96bda52 100644
--- a/identity/java/android/security/identity/CredstorePresentationSession.java
+++ b/identity/java/android/security/identity/CredstorePresentationSession.java
@@ -48,15 +48,18 @@
private byte[] mSessionTranscript = null;
private boolean mOperationHandleSet = false;
private long mOperationHandle = 0;
+ private int mFeatureVersion = 0;
CredstorePresentationSession(Context context,
@IdentityCredentialStore.Ciphersuite int cipherSuite,
CredstoreIdentityCredentialStore store,
- ISession binder) {
+ ISession binder,
+ int featureVersion) {
mContext = context;
mCipherSuite = cipherSuite;
mStore = store;
mBinder = binder;
+ mFeatureVersion = featureVersion;
}
private void ensureEphemeralKeyPair() {
@@ -147,7 +150,7 @@
mBinder.getCredentialForPresentation(credentialName);
credential = new CredstoreIdentityCredential(mContext, credentialName,
mCipherSuite, credstoreCredential,
- this);
+ this, mFeatureVersion);
mCredentialCache.put(credentialName, credential);
credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
diff --git a/identity/java/android/security/identity/CredstoreResultData.java b/identity/java/android/security/identity/CredstoreResultData.java
index 2ef735e..57c0436 100644
--- a/identity/java/android/security/identity/CredstoreResultData.java
+++ b/identity/java/android/security/identity/CredstoreResultData.java
@@ -30,10 +30,11 @@
* data requested from a {@link IdentityCredential}.
*/
class CredstoreResultData extends ResultData {
-
+ int mFeatureVersion = 0;
byte[] mStaticAuthenticationData = null;
byte[] mAuthenticatedData = null;
byte[] mMessageAuthenticationCode = null;
+ byte[] mSignature = null;
private Map<String, Map<String, EntryData>> mData = new LinkedHashMap<>();
@@ -61,6 +62,14 @@
}
@Override
+ @Nullable byte[] getSignature() {
+ if (mFeatureVersion < 202301) {
+ throw new UnsupportedOperationException();
+ }
+ return mSignature;
+ }
+
+ @Override
public @NonNull byte[] getStaticAuthenticationData() {
return mStaticAuthenticationData;
}
@@ -124,13 +133,17 @@
static class Builder {
private CredstoreResultData mResultData;
- Builder(byte[] staticAuthenticationData,
+ Builder(int featureVersion,
+ byte[] staticAuthenticationData,
byte[] authenticatedData,
- byte[] messageAuthenticationCode) {
+ byte[] messageAuthenticationCode,
+ byte[] signature) {
this.mResultData = new CredstoreResultData();
+ this.mResultData.mFeatureVersion = featureVersion;
this.mResultData.mStaticAuthenticationData = staticAuthenticationData;
this.mResultData.mAuthenticatedData = authenticatedData;
this.mResultData.mMessageAuthenticationCode = messageAuthenticationCode;
+ this.mResultData.mSignature = signature;
}
private Map<String, EntryData> getOrCreateInnerMap(String namespaceName) {
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
index d2e7984..1ad70ed 100644
--- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -25,8 +25,9 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.LinkedList;
+import java.util.List;
class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
@@ -61,7 +62,7 @@
throw new RuntimeException("Error decoding certificates", e);
}
- LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+ ArrayList<X509Certificate> x509Certs = new ArrayList<>();
for (Certificate cert : certs) {
x509Certs.add((X509Certificate) cert);
}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index f440b69..2dd9778 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -16,6 +16,7 @@
package android.security.identity;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,6 +26,7 @@
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
/**
@@ -236,14 +238,15 @@
* IntentToRetain = bool
* </pre>
*
- * <p>If the {@code sessionTranscript} parameter is not {@code null}, the X and Y coordinates
- * of the public part of the key-pair previously generated by {@link #createEphemeralKeyPair()}
- * must appear somewhere in the bytes of the CBOR. Each of these coordinates must appear
- * encoded with the most significant bits first and use the exact amount of bits indicated by
- * the key size of the ephemeral keys. For example, if the ephemeral key is using the P-256
- * curve then the 32 bytes for the X coordinate encoded with the most significant bits first
- * must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y
- * coordinate.
+ * <p>If mdoc session encryption is used (e.g. if {@link #createEphemeralKeyPair()} has been
+ * called) and if the {@code sessionTranscript} parameter is not {@code null}, the X and Y
+ * coordinates of the public part of the key-pair previously generated by
+ * {@link #createEphemeralKeyPair()} must appear somewhere in the bytes of the CBOR. Each of
+ * these coordinates must appear encoded with the most significant bits first and use the
+ * exact amount of bits indicated by the key size of the ephemeral keys. For example, if the
+ * ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
+ * the most significant bits first must appear somewhere in {@code sessionTranscript} and
+ * ditto for the 32 bytes for the Y coordinate.
*
* <p>If {@code readerSignature} is not {@code null} it must be the bytes of a
* {@code COSE_Sign1} structure as defined in RFC 8152. For the payload nil shall be used and
@@ -296,7 +299,7 @@
* session transcripts.
* @throws NoAuthenticationKeyAvailableException if authentication keys were never
* provisioned, the method
- * {@link #setAvailableAuthenticationKeys(int, int)}
+ * {@link #setAvailableAuthenticationKeys(int, int, long)}
* was called with {@code keyCount} set to 0,
* the method
* {@link #setAllowUsingExhaustedKeys(boolean)}
@@ -330,19 +333,25 @@
* for which this method has not been called behave as though it had been called wit
* {@code keyCount} 0 and {@code maxUsesPerKey} 1.
*
+ * <p>The effect of this method is like calling
+ * {@link #setAvailableAuthenticationKeys(int, int, long)} with the last parameter is set to 0.
+ *
* @param keyCount The number of active, certified dynamic authentication keys the
* {@code IdentityCredential} will try to keep available. This value
* must be non-negative.
* @param maxUsesPerKey The maximum number of times each of the keys will be used before it's
* eligible for replacement. This value must be greater than zero.
+ * @deprecated Use {@link #setAvailableAuthenticationKeys(int, int, long)} instead.
*/
+ @Deprecated
public abstract void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
/**
* Gets a collection of dynamic authentication keys that need certification.
*
* <p>When there aren't enough certified dynamic authentication keys, either because the key
- * count has been increased or because one or more keys have reached their usage count, this
+ * count has been increased or because one or more keys have reached their usage count or
+ * it if a key is too close to its expiration date, this
* method will generate replacement keys and certificates and return them for issuer
* certification. The issuer certificates and associated static authentication data must then
* be provided back to the Identity Credential using
@@ -400,11 +409,6 @@
* This should only be called for an authenticated key returned by
* {@link #getAuthKeysNeedingCertification()}.
*
- * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
- * fails with {@link UnsupportedOperationException}. See
- * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
- * feature versions.
- *
* @param authenticationKey The dynamic authentication key for which certification and
* associated static
* authentication data is being provided.
@@ -426,7 +430,9 @@
* Get the number of times the dynamic authentication keys have been used.
*
* @return int array of dynamic authentication key usage counts.
+ * @deprecated Use {@link #getAuthenticationKeyMetadata()} instead.
*/
+ @Deprecated
public @NonNull abstract int[] getAuthenticationDataUsageCount();
/**
@@ -519,4 +525,47 @@
public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain,
+ * the number of times each should be used, and the minimum amount of time it's valid for.
+ *
+ * <p>The Identity Credential system will select the least-used dynamic authentication key each
+ * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. Identity Credentials
+ * for which this method has not been called behave as though it had been called wit
+ * {@code keyCount} 0, {@code maxUsesPerKey} 1, and {@code minValidTimeMillis} 0.
+ *
+ * <p>Applications can use {@link #getAuthenticationKeyMetadata()} to get a picture of the
+ * usage andtime left of each configured authentication key. This can be used to determine
+ * how urgent it is recertify new authentication keys via the
+ * {@link #getAuthKeysNeedingCertification()} method.
+ *
+ * @param keyCount The number of active, certified dynamic authentication keys the
+ * {@code IdentityCredential} will try to keep available. This value
+ * must be non-negative.
+ * @param maxUsesPerKey The maximum number of times each of the keys will be used before it's
+ * eligible for replacement. This value must be greater than zero.
+ * @param minValidTimeMillis If a key has less time left than this value it will be eliglible
+ * for replacement. This value must be non-negative.
+ */
+ public void setAvailableAuthenticationKeys(
+ @IntRange(from = 0) int keyCount,
+ @IntRange(from = 1) int maxUsesPerKey,
+ @IntRange(from = 0) long minValidTimeMillis) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get information about dynamic authentication keys.
+ *
+ * <p>The returned list may have <code>null</code> values if certification for the dynamic
+ * authentication key is pending.
+ *
+ * <p>The list is always <code>keyCount</code> elements long.
+ *
+ * @return list of authentication key metadata objects.
+ */
+ public @NonNull List<AuthenticationKeyMetadata> getAuthenticationKeyMetadata() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java
index b34f250..bdb00fdf 100644
--- a/identity/java/android/security/identity/PersonalizationData.java
+++ b/identity/java/android/security/identity/PersonalizationData.java
@@ -18,10 +18,11 @@
import android.annotation.NonNull;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
+import java.util.List;
/**
* An object that holds personalization data.
@@ -38,7 +39,7 @@
private PersonalizationData() {
}
- private LinkedList<AccessControlProfile> mProfiles = new LinkedList<>();
+ private ArrayList<AccessControlProfile> mProfiles = new ArrayList<>();
private LinkedHashMap<String, NamespaceData> mNamespaces = new LinkedHashMap<>();
diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java
index 6cde611..f392e12 100644
--- a/identity/java/android/security/identity/PresentationSession.java
+++ b/identity/java/android/security/identity/PresentationSession.java
@@ -73,7 +73,8 @@
* <p>If called, this must be called before any calls to
* {@link #getCredentialData(String, CredentialDataRequest)}.
*
- * <p>The X and Y coordinates of the public part of the key-pair returned by {@link
+ * <p>If mdoc session encryption is used (e.g. if {@link #getEphemeralKeyPair()} has been
+ * called) then the X and Y coordinates of the public part of the key-pair returned by {@link
* #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR. Each of
* these coordinates must appear encoded with the most significant bits first and use the exact
* amount of bits indicated by the key size of the ephemeral keys. For example, if the
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index d46f985..8a0e56e 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -134,6 +134,10 @@
*/
public abstract @Nullable byte[] getMessageAuthenticationCode();
+ @Nullable byte[] getSignature() {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Returns the static authentication data associated with the dynamic authentication
* key used to sign or MAC the data returned by {@link #getAuthenticatedData()}.
diff --git a/media/java/android/media/DeniedByServerException.java b/media/java/android/media/DeniedByServerException.java
index 9c1633a..98903ec 100644
--- a/media/java/android/media/DeniedByServerException.java
+++ b/media/java/android/media/DeniedByServerException.java
@@ -24,4 +24,11 @@
public DeniedByServerException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * @hide
+ */
+ public DeniedByServerException(String message, int vendorError, int oemError, int context) {
+ super(message, vendorError, oemError, context);
+ }
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 220232d..8e8ed6c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2472,10 +2472,22 @@
/**
* Thrown when a crypto error occurs while queueing a secure input buffer.
*/
- public final static class CryptoException extends RuntimeException {
+ public final static class CryptoException extends RuntimeException
+ implements MediaDrmThrowable {
public CryptoException(int errorCode, @Nullable String detailMessage) {
- super(detailMessage);
+ this(detailMessage, errorCode, 0, 0, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public CryptoException(String message, int errorCode, int vendorError, int oemError,
+ int errorContext) {
+ super(message);
mErrorCode = errorCode;
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
}
/**
@@ -2594,7 +2606,22 @@
return mErrorCode;
}
- private int mErrorCode;
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
+ private final int mErrorCode, mVendorError, mOemError, mErrorContext;
}
/**
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 30d90a8..55de0b3 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -981,8 +981,20 @@
continue;
}
- // AAC does not use levels
- if (level == null || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ // No specific level requested
+ if (level == null) {
+ return true;
+ }
+
+ // AAC doesn't use levels
+ if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ return true;
+ }
+
+ // DTS doesn't use levels
+ if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS)
+ || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_HD)
+ || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_UHD)) {
return true;
}
@@ -1410,6 +1422,7 @@
int[] sampleRates = null;
Range<Integer> sampleRateRange = null, bitRates = null;
int maxChannels = MAX_INPUT_CHANNEL_COUNT;
+ CodecProfileLevel[] profileLevels = mParent.profileLevels;
String mime = mParent.getMimeType();
if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) {
@@ -1473,6 +1486,53 @@
sampleRates = new int[] { 44100, 48000, 96000, 192000 };
bitRates = Range.create(16000, 2688000);
maxChannels = 24;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS)) {
+ sampleRates = new int[] { 44100, 48000 };
+ bitRates = Range.create(96000, 1524000);
+ maxChannels = 6;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_HD)) {
+ for (CodecProfileLevel profileLevel: profileLevels) {
+ switch (profileLevel.profile) {
+ case CodecProfileLevel.DTS_HDProfileLBR:
+ sampleRates = new int[]{ 22050, 24000, 44100, 48000 };
+ bitRates = Range.create(32000, 768000);
+ break;
+ case CodecProfileLevel.DTS_HDProfileHRA:
+ case CodecProfileLevel.DTS_HDProfileMA:
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ break;
+ default:
+ Log.w(TAG, "Unrecognized profile "
+ + profileLevel.profile + " for " + mime);
+ mParent.mError |= ERROR_UNRECOGNIZED;
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ }
+ }
+ maxChannels = 8;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_UHD)) {
+ for (CodecProfileLevel profileLevel: profileLevels) {
+ switch (profileLevel.profile) {
+ case CodecProfileLevel.DTS_UHDProfileP2:
+ sampleRates = new int[]{ 48000 };
+ bitRates = Range.create(96000, 768000);
+ maxChannels = 10;
+ break;
+ case CodecProfileLevel.DTS_UHDProfileP1:
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ maxChannels = 32;
+ break;
+ default:
+ Log.w(TAG, "Unrecognized profile "
+ + profileLevel.profile + " for " + mime);
+ mParent.mError |= ERROR_UNRECOGNIZED;
+ sampleRates = new int[]{ 44100, 48000, 88200, 96000, 176400, 192000 };
+ bitRates = Range.create(96000, 24500000);
+ maxChannels = 32;
+ }
+ }
} else {
Log.w(TAG, "Unsupported mime " + mime);
mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/MediaCryptoException.java b/media/java/android/media/MediaCryptoException.java
index 32ddf47..2a472fb 100644
--- a/media/java/android/media/MediaCryptoException.java
+++ b/media/java/android/media/MediaCryptoException.java
@@ -22,8 +22,35 @@
* Exception thrown if MediaCrypto object could not be instantiated or
* if unable to perform an operation on the MediaCrypto object.
*/
-public final class MediaCryptoException extends Exception {
+public final class MediaCryptoException extends Exception implements MediaDrmThrowable {
public MediaCryptoException(@Nullable String detailMessage) {
- super(detailMessage);
+ this(detailMessage, 0, 0, 0);
}
+
+ /**
+ * @hide
+ */
+ public MediaCryptoException(String message, int vendorError, int oemError, int errorContext) {
+ super(message);
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
+ }
+
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
+ private final int mVendorError, mOemError, mErrorContext;
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2a04ebb..a3fa43d 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -664,21 +664,33 @@
* strategy and details about each possible return value from {@link
* MediaDrmStateException#getErrorCode()}.
*/
- public static final class MediaDrmStateException extends java.lang.IllegalStateException {
- private final int mErrorCode;
+ public static final class MediaDrmStateException extends java.lang.IllegalStateException
+ implements MediaDrmThrowable {
+ private final int mErrorCode, mVendorError, mOemError, mErrorContext;
private final String mDiagnosticInfo;
/**
* @hide
*/
public MediaDrmStateException(int errorCode, @Nullable String detailMessage) {
+ this(detailMessage, errorCode, 0, 0, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public MediaDrmStateException(String detailMessage, int errorCode,
+ int vendorError, int oemError, int errorContext) {
super(detailMessage);
mErrorCode = errorCode;
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
// TODO get this from DRM session
final String sign = errorCode < 0 ? "neg_" : "";
mDiagnosticInfo =
- "android.media.MediaDrm.error_" + sign + Math.abs(errorCode);
+ "android.media.MediaDrm.error_" + sign + Math.abs(errorCode);
}
@@ -696,6 +708,21 @@
return mErrorCode;
}
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
/**
* Returns true if the {@link MediaDrmStateException} is a transient
* issue, perhaps due to resource constraints, and that the operation
@@ -727,10 +754,22 @@
* {@link #isTransient()} to determine whether the app should retry the
* failing operation.
*/
- public static final class SessionException extends RuntimeException {
+ public static final class SessionException extends RuntimeException
+ implements MediaDrmThrowable {
public SessionException(int errorCode, @Nullable String detailMessage) {
+ this(detailMessage, errorCode, 0, 0, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public SessionException(String detailMessage, int errorCode, int vendorError, int oemError,
+ int errorContext) {
super(detailMessage);
mErrorCode = errorCode;
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
}
/**
@@ -769,6 +808,21 @@
return mErrorCode;
}
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
/**
* Returns true if the {@link SessionException} is a transient
* issue, perhaps due to resource constraints, and that the operation
@@ -779,7 +833,7 @@
return mErrorCode == ERROR_RESOURCE_CONTENTION;
}
- private final int mErrorCode;
+ private final int mErrorCode, mVendorError, mOemError, mErrorContext;
}
/**
diff --git a/media/java/android/media/MediaDrmException.java b/media/java/android/media/MediaDrmException.java
index d547574..58c5dd0 100644
--- a/media/java/android/media/MediaDrmException.java
+++ b/media/java/android/media/MediaDrmException.java
@@ -19,8 +19,35 @@
/**
* Base class for MediaDrm exceptions
*/
-public class MediaDrmException extends Exception {
+public class MediaDrmException extends Exception implements MediaDrmThrowable {
public MediaDrmException(String detailMessage) {
- super(detailMessage);
+ this(detailMessage, 0, 0, 0);
}
+
+ /**
+ * @hide
+ */
+ public MediaDrmException(String message, int vendorError, int oemError, int errorContext) {
+ super(message);
+ mVendorError = vendorError;
+ mOemError = oemError;
+ mErrorContext = errorContext;
+ }
+
+ @Override
+ public int getVendorError() {
+ return mVendorError;
+ }
+
+ @Override
+ public int getOemError() {
+ return mOemError;
+ }
+
+ @Override
+ public int getErrorContext() {
+ return mErrorContext;
+ }
+
+ private final int mVendorError, mOemError, mErrorContext;
}
diff --git a/media/java/android/media/MediaDrmResetException.java b/media/java/android/media/MediaDrmResetException.java
index 3b2da1e..ccd723b 100644
--- a/media/java/android/media/MediaDrmResetException.java
+++ b/media/java/android/media/MediaDrmResetException.java
@@ -21,7 +21,7 @@
* due to a restart of the mediaserver process. To continue, the app must
* release the MediaDrm object, then create and initialize a new one.
*/
-public class MediaDrmResetException extends IllegalStateException {
+public class MediaDrmResetException extends IllegalStateException implements MediaDrmThrowable {
public MediaDrmResetException(String detailMessage) {
super(detailMessage);
}
diff --git a/media/java/android/media/MediaDrmThrowable.java b/media/java/android/media/MediaDrmThrowable.java
new file mode 100644
index 0000000..38480d7
--- /dev/null
+++ b/media/java/android/media/MediaDrmThrowable.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.media;
+
+/**
+ * A @{@link Throwable} thrown from {@link MediaDrm} or @{@link MediaCrypto} APIs
+ */
+public interface MediaDrmThrowable {
+ /**
+ * Returns {@link MediaDrm} plugin vendor defined error code associated with this {@link
+ * MediaDrmThrowable}.
+ * <p>
+ * Please consult the {@link MediaDrm} plugin vendor for details on the error code.
+ *
+ * @return an error code defined by the {@link MediaDrm} plugin vendor if available,
+ * otherwise 0.
+ */
+ public default int getVendorError() {
+ return 0;
+ }
+
+ /**
+ * Returns OEM or SOC specific error code associated with this {@link
+ * MediaDrmThrowable}.
+ * <p>
+ * Please consult the {@link MediaDrm} plugin, chip, or device vendor for details on the
+ * error code.
+ *
+ * @return an OEM or SOC specific error code if available, otherwise 0.
+ */
+ public default int getOemError() {
+ return 0;
+ }
+
+ /**
+ * Returns {@link MediaDrm} plugin vendor defined error context associated with this {@link
+ * MediaDrmThrowable}.
+ * <p>
+ * Please consult the {@link MediaDrm} plugin vendor for details on the error context.
+ *
+ * @return an opaque integer that would help the @{@link MediaDrm} vendor locate the
+ * source of the error if available, otherwise 0.
+ */
+ public default int getErrorContext() {
+ return 0;
+ }
+
+}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index be9862b..b51c018 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -174,11 +174,20 @@
public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
/** MIME type for MPEG-H Audio single stream, encapsulated in MHAS */
public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
- /** MIME type for DTS (up to 5.1 channels) audio stream. */
+ /** MIME type for DTS Digital Surround (up to 5.1 channels) audio stream, aka DTS-CA. */
public static final String MIMETYPE_AUDIO_DTS = "audio/vnd.dts";
- /** MIME type for DTS HD (up to 7.1 channels) audio stream. */
+ /**
+ * MIME type for DTS HD (up to 7.1 channels) audio stream.
+ * With codec profile DTS_HDProfileHRA represents DTS HD High Resolution Audio.
+ * With codec profile DTS_HDProfileMA represents DTS HD Master Audio.
+ * With codec profile DTS_HDProfileLBR represents DTS Express.
+ */
public static final String MIMETYPE_AUDIO_DTS_HD = "audio/vnd.dts.hd";
- /** MIME type for DTS UHD (object-based) audio stream. */
+ /**
+ * MIME type for DTS UHD (object-based) audio stream, aka DTS:X.
+ * With codec profile DTS_UHDProfileP1 represents DTS-UHD P1.
+ * With codec profile DTS_UHDProfileP2 represents DTS-UHD P2.
+ */
public static final String MIMETYPE_AUDIO_DTS_UHD = "audio/vnd.dts.uhd";
/** MIME type for Dynamic Resolution Adaptation (DRA) audio stream. */
public static final String MIMETYPE_AUDIO_DRA = "audio/vnd.dra";
diff --git a/media/java/android/media/NotProvisionedException.java b/media/java/android/media/NotProvisionedException.java
index 32b8151..4b5a816 100644
--- a/media/java/android/media/NotProvisionedException.java
+++ b/media/java/android/media/NotProvisionedException.java
@@ -26,4 +26,11 @@
public NotProvisionedException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * @hide
+ */
+ public NotProvisionedException(String message, int vendorError, int oemError, int context) {
+ super(message, vendorError, oemError, context);
+ }
}
diff --git a/media/java/android/media/ResourceBusyException.java b/media/java/android/media/ResourceBusyException.java
index a5abe21..7aaf7eb 100644
--- a/media/java/android/media/ResourceBusyException.java
+++ b/media/java/android/media/ResourceBusyException.java
@@ -24,4 +24,11 @@
public ResourceBusyException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * @hide
+ */
+ public ResourceBusyException(String message, int vendorError, int oemError, int context) {
+ super(message, vendorError, oemError, context);
+ }
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 1183ca3..d8705a7 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1285,7 +1285,7 @@
CHECK(clazz.get() != NULL);
jmethodID constructID =
- env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
+ env->GetMethodID(clazz.get(), "<init>", "(Ljava/lang/String;IIII)V");
CHECK(constructID != NULL);
std::string defaultMsg = "Unknown Error";
@@ -1335,14 +1335,14 @@
break;
}
- std::string msgStr(msg != NULL ? msg : defaultMsg.c_str());
- if (crypto != NULL) {
- msgStr = DrmUtils::GetExceptionMessage(err, msgStr.c_str(), crypto);
- }
- jstring msgObj = env->NewStringUTF(msgStr.c_str());
+ std::string originalMsg(msg != NULL ? msg : defaultMsg.c_str());
+ DrmStatus dStatus(err, originalMsg.c_str());
+ std::string detailedMsg(DrmUtils::GetExceptionMessage(dStatus, defaultMsg.c_str(), crypto));
+ jstring msgObj = env->NewStringUTF(detailedMsg.c_str());
jthrowable exception =
- (jthrowable)env->NewObject(clazz.get(), constructID, jerr, msgObj);
+ (jthrowable)env->NewObject(clazz.get(), constructID, msgObj, jerr,
+ dStatus.getCdmErr(), dStatus.getOemErr(), dStatus.getContext());
env->Throw(exception);
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 2f4dd8f..681d76a 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -244,6 +244,20 @@
}
return arrayList;
}
+
+int drmThrowException(JNIEnv* env, const char *className, const DrmStatus &err, const char *msg) {
+ using namespace android::jnihelp;
+ jstring _detailMessage = CreateExceptionMsg(env, msg);
+ int _status = ThrowException(env, className, "(Ljava/lang/String;III)V",
+ _detailMessage,
+ err.getCdmErr(),
+ err.getOemErr(),
+ err.getContext());
+ if (_detailMessage != NULL) {
+ env->DeleteLocalRef(_detailMessage);
+ }
+ return _status;
+}
} // namespace anonymous
// ----------------------------------------------------------------------------
@@ -388,18 +402,18 @@
return static_cast<jint>(err);
}
-static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
- ALOGE("Illegal state exception: %s (%d)", msg, err);
+static void throwStateException(JNIEnv *env, const char *msg, const DrmStatus &err) {
+ ALOGE("Illegal state exception: %s (%d)", msg, static_cast<status_t>(err));
jint jerr = MediaErrorToJavaError(err);
jobject exception = env->NewObject(gFields.stateException.classId,
- gFields.stateException.init, static_cast<int>(jerr),
- env->NewStringUTF(msg));
+ gFields.stateException.init, env->NewStringUTF(msg), static_cast<int>(jerr),
+ err.getCdmErr(), err.getOemErr(), err.getContext());
env->Throw(static_cast<jthrowable>(exception));
}
-static void throwSessionException(JNIEnv *env, const char *msg, status_t err) {
- ALOGE("Session exception: %s (%d)", msg, err);
+static void throwSessionException(JNIEnv *env, const char *msg, const DrmStatus &err) {
+ ALOGE("Session exception: %s (%d)", msg, static_cast<status_t>(err));
jint jErrorCode = 0;
switch(err) {
@@ -411,10 +425,13 @@
}
jobject exception = env->NewObject(gFields.sessionException.classId,
- gFields.sessionException.init, static_cast<int>(err),
- env->NewStringUTF(msg));
+ gFields.sessionException.init,
+ env->NewStringUTF(msg),
+ jErrorCode,
+ err.getCdmErr(),
+ err.getOemErr(),
+ err.getContext());
- env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode);
env->Throw(static_cast<jthrowable>(exception));
}
@@ -423,7 +440,7 @@
}
static bool throwExceptionAsNecessary(
- JNIEnv *env, const sp<IDrm> &drm, status_t err, const char *msg = NULL) {
+ JNIEnv *env, const sp<IDrm> &drm, const DrmStatus &err, const char *msg = NULL) {
std::string msgStr;
if (drm != NULL && err != OK) {
msgStr = DrmUtils::GetExceptionMessage(err, msg, drm);
@@ -437,13 +454,13 @@
jniThrowException(env, "java/lang/UnsupportedOperationException", msg);
return true;
} else if (err == ERROR_DRM_NOT_PROVISIONED) {
- jniThrowException(env, "android/media/NotProvisionedException", msg);
+ drmThrowException(env, "android/media/NotProvisionedException", err, msg);
return true;
} else if (err == ERROR_DRM_RESOURCE_BUSY) {
- jniThrowException(env, "android/media/ResourceBusyException", msg);
+ drmThrowException(env, "android/media/ResourceBusyException", err, msg);
return true;
} else if (err == ERROR_DRM_DEVICE_REVOKED) {
- jniThrowException(env, "android/media/DeniedByServerException", msg);
+ drmThrowException(env, "android/media/DeniedByServerException", err, msg);
return true;
} else if (err == DEAD_OBJECT) {
jniThrowException(env, "android/media/MediaDrmResetException", msg);
@@ -493,7 +510,7 @@
return NULL;
}
- status_t err = drm->createPlugin(uuid, appPackageName);
+ DrmStatus err = drm->createPlugin(uuid, appPackageName);
if (err != OK) {
return NULL;
@@ -915,11 +932,11 @@
gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
- GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+ GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(Ljava/lang/String;IIII)V");
gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
FIND_CLASS(clazz, "android/media/MediaDrm$SessionException");
- GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+ GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(Ljava/lang/String;IIII)V");
gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I");
@@ -1062,7 +1079,7 @@
return NULL;
}
- status_t err = drm->openSession(level, sessionId);
+ DrmStatus err = drm->openSession(level, sessionId);
if (throwExceptionAsNecessary(env, drm, err, "Failed to open session")) {
return NULL;
@@ -1081,7 +1098,7 @@
Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
- status_t err = drm->closeSession(sessionId);
+ DrmStatus err = drm->closeSession(sessionId);
throwExceptionAsNecessary(env, drm, err, "Failed to close session");
}
@@ -1133,8 +1150,8 @@
String8 defaultUrl;
DrmPlugin::KeyRequestType keyRequestType;
- status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
- keyType, optParams, request, defaultUrl, &keyRequestType);
+ DrmStatus err = drm->getKeyRequest(sessionId, initData, mimeType, keyType, optParams, request,
+ defaultUrl, &keyRequestType);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get key request")) {
return NULL;
@@ -1204,7 +1221,7 @@
Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
Vector<uint8_t> keySetId;
- status_t err = drm->provideKeyResponse(sessionId, response, keySetId);
+ DrmStatus err = drm->provideKeyResponse(sessionId, response, keySetId);
if (throwExceptionAsNecessary(env, drm, err, "Failed to handle key response")) {
return NULL;
@@ -1228,7 +1245,7 @@
Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
- status_t err = drm->removeKeys(keySetId);
+ DrmStatus err = drm->removeKeys(keySetId);
throwExceptionAsNecessary(env, drm, err, "Failed to remove keys");
}
@@ -1251,7 +1268,7 @@
Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
- status_t err = drm->restoreKeys(sessionId, keySetId);
+ DrmStatus err = drm->restoreKeys(sessionId, keySetId);
throwExceptionAsNecessary(env, drm, err, "Failed to restore keys");
}
@@ -1267,7 +1284,7 @@
KeyedVector<String8, String8> infoMap;
- status_t err = drm->queryKeyStatus(sessionId, infoMap);
+ DrmStatus err = drm->queryKeyStatus(sessionId, infoMap);
if (throwExceptionAsNecessary(env, drm, err, "Failed to query key status")) {
return NULL;
@@ -1297,7 +1314,7 @@
}
String8 certAuthority = JStringToString8(env, jcertAuthority);
- status_t err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl);
+ DrmStatus err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get provision request")) {
return NULL;
@@ -1338,7 +1355,7 @@
Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
Vector<uint8_t> certificate, wrappedKey;
- status_t err = drm->provideProvisionResponse(response, certificate, wrappedKey);
+ DrmStatus err = drm->provideProvisionResponse(response, certificate, wrappedKey);
// Fill out return obj
jclass clazz = gFields.certificateClassId;
@@ -1368,7 +1385,7 @@
List<Vector<uint8_t>> secureStops;
- status_t err = drm->getSecureStops(secureStops);
+ DrmStatus err = drm->getSecureStops(secureStops);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stops")) {
return NULL;
@@ -1387,7 +1404,7 @@
List<Vector<uint8_t>> secureStopIds;
- status_t err = drm->getSecureStopIds(secureStopIds);
+ DrmStatus err = drm->getSecureStopIds(secureStopIds);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop Ids")) {
return NULL;
@@ -1406,7 +1423,7 @@
Vector<uint8_t> secureStop;
- status_t err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop);
+ DrmStatus err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop")) {
return NULL;
@@ -1425,7 +1442,7 @@
Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease));
- status_t err = drm->releaseSecureStops(ssRelease);
+ DrmStatus err = drm->releaseSecureStops(ssRelease);
throwExceptionAsNecessary(env, drm, err, "Failed to release secure stops");
}
@@ -1438,7 +1455,7 @@
return;
}
- status_t err = drm->removeSecureStop(JByteArrayToVector(env, ssid));
+ DrmStatus err = drm->removeSecureStop(JByteArrayToVector(env, ssid));
throwExceptionAsNecessary(env, drm, err, "Failed to remove secure stop");
}
@@ -1451,7 +1468,7 @@
return;
}
- status_t err = drm->removeAllSecureStops();
+ DrmStatus err = drm->removeAllSecureStops();
throwExceptionAsNecessary(env, drm, err, "Failed to remove all secure stops");
}
@@ -1490,7 +1507,7 @@
DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpNone;
DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpNone;
- status_t err = drm->getHdcpLevels(&connected, &max);
+ DrmStatus err = drm->getHdcpLevels(&connected, &max);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) {
return gHdcpLevels.kHdcpLevelUnknown;
@@ -1509,7 +1526,7 @@
DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpLevelUnknown;
DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpLevelUnknown;
- status_t err = drm->getHdcpLevels(&connected, &max);
+ DrmStatus err = drm->getHdcpLevels(&connected, &max);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) {
return gHdcpLevels.kHdcpLevelUnknown;
@@ -1526,7 +1543,7 @@
}
uint32_t open = 0, max = 0;
- status_t err = drm->getNumberOfSessions(&open, &max);
+ DrmStatus err = drm->getNumberOfSessions(&open, &max);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) {
return 0;
@@ -1543,7 +1560,7 @@
}
uint32_t open = 0, max = 0;
- status_t err = drm->getNumberOfSessions(&open, &max);
+ DrmStatus err = drm->getNumberOfSessions(&open, &max);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) {
return 0;
@@ -1563,7 +1580,7 @@
DrmPlugin::SecurityLevel level = DrmPlugin::kSecurityLevelUnknown;
- status_t err = drm->getSecurityLevel(sessionId, &level);
+ DrmStatus err = drm->getSecurityLevel(sessionId, &level);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get security level")) {
return gSecurityLevels.kSecurityLevelUnknown;
@@ -1595,7 +1612,7 @@
List<Vector<uint8_t> > keySetIds;
- status_t err = drm->getOfflineLicenseKeySetIds(keySetIds);
+ DrmStatus err = drm->getOfflineLicenseKeySetIds(keySetIds);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline key set Ids")) {
return NULL;
@@ -1612,7 +1629,7 @@
return;
}
- status_t err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId));
+ DrmStatus err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId));
throwExceptionAsNecessary(env, drm, err, "Failed to remove offline license");
}
@@ -1629,7 +1646,7 @@
DrmPlugin::OfflineLicenseState state = DrmPlugin::kOfflineLicenseStateUnknown;
- status_t err = drm->getOfflineLicenseState(keySetId, &state);
+ DrmStatus err = drm->getOfflineLicenseState(keySetId, &state);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline license state")) {
return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
@@ -1662,7 +1679,7 @@
String8 name = JStringToString8(env, jname);
String8 value;
- status_t err = drm->getPropertyString(name, value);
+ DrmStatus err = drm->getPropertyString(name, value);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) {
return NULL;
@@ -1688,7 +1705,7 @@
String8 name = JStringToString8(env, jname);
Vector<uint8_t> value;
- status_t err = drm->getPropertyByteArray(name, value);
+ DrmStatus err = drm->getPropertyByteArray(name, value);
if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) {
return NULL;
@@ -1720,7 +1737,7 @@
String8 name = JStringToString8(env, jname);
String8 value = JStringToString8(env, jvalue);
- status_t err = drm->setPropertyString(name, value);
+ DrmStatus err = drm->setPropertyString(name, value);
throwExceptionAsNecessary(env, drm, err, "Failed to set property");
}
@@ -1748,7 +1765,7 @@
String8 name = JStringToString8(env, jname);
Vector<uint8_t> value = JByteArrayToVector(env, jvalue);
- status_t err = drm->setPropertyByteArray(name, value);
+ DrmStatus err = drm->setPropertyByteArray(name, value);
throwExceptionAsNecessary(env, drm, err, "Failed to set property");
}
@@ -1772,7 +1789,7 @@
Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
String8 algorithm = JStringToString8(env, jalgorithm);
- status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
+ DrmStatus err = drm->setCipherAlgorithm(sessionId, algorithm);
throwExceptionAsNecessary(env, drm, err, "Failed to set cipher algorithm");
}
@@ -1796,7 +1813,7 @@
Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
String8 algorithm = JStringToString8(env, jalgorithm);
- status_t err = drm->setMacAlgorithm(sessionId, algorithm);
+ DrmStatus err = drm->setMacAlgorithm(sessionId, algorithm);
throwExceptionAsNecessary(env, drm, err, "Failed to set mac algorithm");
}
@@ -1824,7 +1841,7 @@
Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
Vector<uint8_t> output;
- status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
+ DrmStatus err = drm->encrypt(sessionId, keyId, input, iv, output);
if (throwExceptionAsNecessary(env, drm, err, "Failed to encrypt")) {
return NULL;
@@ -1855,7 +1872,7 @@
Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
Vector<uint8_t> output;
- status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
+ DrmStatus err = drm->decrypt(sessionId, keyId, input, iv, output);
if (throwExceptionAsNecessary(env, drm, err, "Failed to decrypt")) {
return NULL;
}
@@ -1884,7 +1901,7 @@
Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
Vector<uint8_t> signature;
- status_t err = drm->sign(sessionId, keyId, message, signature);
+ DrmStatus err = drm->sign(sessionId, keyId, message, signature);
if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) {
return NULL;
@@ -1915,7 +1932,7 @@
Vector<uint8_t> signature(JByteArrayToVector(env, jsignature));
bool match;
- status_t err = drm->verify(sessionId, keyId, message, signature, match);
+ DrmStatus err = drm->verify(sessionId, keyId, message, signature, match);
throwExceptionAsNecessary(env, drm, err, "Failed to verify");
return match;
@@ -1933,7 +1950,7 @@
// Retrieve current metrics snapshot from drm.
PersistableBundle metrics;
sp<IDrmMetricsConsumer> consumer(new DrmMetricsConsumer(&metrics));
- status_t err = drm->getMetrics(consumer);
+ DrmStatus err = drm->getMetrics(consumer);
if (err != OK) {
ALOGE("getMetrics failed: %d", (int)err);
return (jobject) NULL;
@@ -1964,7 +1981,7 @@
Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
Vector<uint8_t> signature;
- status_t err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature);
+ DrmStatus err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature);
if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) {
return NULL;
@@ -1993,7 +2010,7 @@
}
bool required = false;
- status_t err = OK;
+ DrmStatus err = OK;
if (securityLevel == DrmPlugin::kSecurityLevelMax) {
err = drm->requiresSecureDecoder(mimeType.c_str(), &required);
} else {
@@ -2019,7 +2036,7 @@
if (jplaybackId != NULL) {
playbackId = JStringToString8(env, jplaybackId);
}
- status_t err = drm->setPlaybackId(sessionId, playbackId.c_str());
+ DrmStatus err = drm->setPlaybackId(sessionId, playbackId.c_str());
throwExceptionAsNecessary(env, drm, err, "Failed to set playbackId");
}
@@ -2031,7 +2048,7 @@
}
Vector<drm::V1_4::LogMessage> logs;
- status_t err = drm->getLogMessages(logs);
+ DrmStatus err = drm->getLogMessages(logs);
ALOGI("drm->getLogMessages %zu logs", logs.size());
if (throwExceptionAsNecessary(env, drm, err, "Failed to get log messages")) {
return NULL;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
index 9dfc8ea..c0117b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
@@ -22,13 +22,16 @@
import android.util.Log;
import android.util.LruCache;
+import androidx.annotation.VisibleForTesting;
+
/**
* Cache app icon for management.
*/
public class AppIconCacheManager {
private static final String TAG = "AppIconCacheManager";
private static final float CACHE_RATIO = 0.1f;
- private static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
+ @VisibleForTesting
+ static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
private static final String DELIMITER = ":";
private static AppIconCacheManager sAppIconCacheManager;
private final LruCache<String, Drawable> mDrawableCache;
@@ -109,4 +112,25 @@
private static int getMaxCacheInKb() {
return Math.round(CACHE_RATIO * Runtime.getRuntime().maxMemory() / 1024);
}
+
+ /**
+ * Clears as much memory as possible.
+ *
+ * @see android.content.ComponentCallbacks2#onTrimMemory(int)
+ */
+ public void trimMemory(int level) {
+ if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
+ // Time to clear everything
+ if (sAppIconCacheManager != null) {
+ sAppIconCacheManager.mDrawableCache.trimToSize(0);
+ }
+ } else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+ || level == android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+ // Tough time but still affordable, clear half of the cache
+ if (sAppIconCacheManager != null) {
+ final int maxSize = sAppIconCacheManager.mDrawableCache.maxSize();
+ sAppIconCacheManager.mDrawableCache.trimToSize(maxSize / 2);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
index 64f8bef..1b0e1f1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
@@ -34,11 +34,21 @@
public class AppIconCacheManagerTest {
private static final String APP_PACKAGE_NAME = "com.test.app";
+ private static final String APP_PACKAGE_NAME1 = "com.test.app1";
+ private static final String APP_PACKAGE_NAME2 = "com.test.app2";
+ private static final String APP_PACKAGE_NAME3 = "com.test.app3";
private static final int APP_UID = 9999;
@Mock
private Drawable mIcon;
+ @Mock
+ private Drawable mIcon1;
+ @Mock
+ private Drawable mIcon2;
+ @Mock
+ private Drawable mIcon3;
+
private AppIconCacheManager mAppIconCacheManager;
@Before
@@ -48,6 +58,29 @@
doReturn(10).when(mIcon).getIntrinsicHeight();
doReturn(10).when(mIcon).getIntrinsicWidth();
doReturn(mIcon).when(mIcon).mutate();
+
+ // Algorithm for trim memory test:
+ // The real maxsize is defined by AppIconCacheManager.MAX_CACHE_SIZE_IN_KB, and the size
+ // of each element is calculated as following:
+ // n * n * 4 / 1024
+ // In the testcase, we want to mock the maxsize of LruCache is 3, so the formula calculating
+ // the size of each element will be like:
+ // n * n * 4 / 1024 = maxsize / 3
+ // Thus, n = square_root(maxsize / 3 * 1024 / 4), which can be used as an icon size.
+ final int iconSize =
+ (int) Math.sqrt(AppIconCacheManager.MAX_CACHE_SIZE_IN_KB / 3f * 1024f / 4f);
+
+ doReturn(iconSize).when(mIcon1).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon1).getIntrinsicWidth();
+ doReturn(mIcon1).when(mIcon1).mutate();
+
+ doReturn(iconSize).when(mIcon2).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon2).getIntrinsicWidth();
+ doReturn(mIcon2).when(mIcon2).mutate();
+
+ doReturn(iconSize).when(mIcon3).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon3).getIntrinsicWidth();
+ doReturn(mIcon3).when(mIcon3).mutate();
}
@After
@@ -106,4 +139,41 @@
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
+
+ @Test
+ public void trimMemory_levelSatisfied_shouldNotCacheIcon() {
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
+
+ // Expected to trim size to 0
+ final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ mAppIconCacheManager.trimMemory(level);
+
+ // None of the elements should be cached
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
+ }
+
+ @Test
+ public void trimMemory_levelSatisfied_shouldCacheAtLeastHalf() {
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
+
+ // Get the last element
+ mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID);
+
+ // Expected to trim size to half of it, which is int( 3 / 2 ) = 1
+ final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+ mAppIconCacheManager.trimMemory(level);
+
+ // There should be only one cached element, which is the last recently used one
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNotNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/OWNERS b/packages/SystemUI/src/com/android/systemui/notetask/OWNERS
new file mode 100644
index 0000000..7ccb316
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1254381
+azappone@google.com
+achalke@google.com
+juliacr@google.com
+madym@google.com
+mgalhardo@google.com
+petrcermak@google.com
+vanjan@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/OWNERS b/packages/SystemUI/src/com/android/systemui/stylus/OWNERS
new file mode 100644
index 0000000..7ccb316
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stylus/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1254381
+azappone@google.com
+achalke@google.com
+juliacr@google.com
+madym@google.com
+mgalhardo@google.com
+petrcermak@google.com
+vanjan@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index bf7c459..55fa4c0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -429,6 +429,11 @@
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0;
changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
+ } else if (stream == AudioManager.STREAM_VOICE_CALL) {
+ final boolean routedToBluetooth =
+ (mAudio.getDevicesForStream(AudioManager.STREAM_VOICE_CALL)
+ & AudioManager.DEVICE_OUT_BLE_HEADSET) != 0;
+ changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
}
return changed;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 903aba1..198d022 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1677,6 +1677,7 @@
if (ss.level == row.requestedLevel) {
row.requestedLevel = -1;
}
+ final boolean isVoiceCallStream = row.stream == AudioManager.STREAM_VOICE_CALL;
final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY;
final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
@@ -1721,8 +1722,12 @@
} else if (isRingSilent || zenMuted) {
iconRes = row.iconMuteRes;
} else if (ss.routedToBluetooth) {
- iconRes = isStreamMuted(ss) ? R.drawable.ic_volume_media_bt_mute
- : R.drawable.ic_volume_media_bt;
+ if (isVoiceCallStream) {
+ iconRes = R.drawable.ic_volume_bt_sco;
+ } else {
+ iconRes = isStreamMuted(ss) ? R.drawable.ic_volume_media_bt_mute
+ : R.drawable.ic_volume_media_bt;
+ }
} else if (isStreamMuted(ss)) {
iconRes = ss.muted ? R.drawable.ic_volume_media_off : row.iconMuteRes;
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS
new file mode 100644
index 0000000..7ccb316
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1254381
+azappone@google.com
+achalke@google.com
+juliacr@google.com
+madym@google.com
+mgalhardo@google.com
+petrcermak@google.com
+vanjan@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS
new file mode 100644
index 0000000..7ccb316
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1254381
+azappone@google.com
+achalke@google.com
+juliacr@google.com
+madym@google.com
+mgalhardo@google.com
+petrcermak@google.com
+vanjan@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index aaf2188..acf1088 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -167,6 +167,34 @@
}
@Test
+ public void testVolumeChangeW_deviceOutFromBLEHeadset_doStateChanged() {
+ mVolumeController.setDeviceInteractive(false);
+ when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
+ WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ when(mAudioManager.getDevicesForStream(AudioManager.STREAM_VOICE_CALL)).thenReturn(
+ AudioManager.DEVICE_OUT_BLE_HEADSET);
+
+ mVolumeController.onVolumeChangedW(
+ AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI);
+
+ verify(mCallback, times(1)).onStateChanged(any());
+ }
+
+ @Test
+ public void testVolumeChangeW_deviceOutFromA2DP_doStateChanged() {
+ mVolumeController.setDeviceInteractive(false);
+ when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
+ WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ when(mAudioManager.getDevicesForStream(AudioManager.STREAM_VOICE_CALL)).thenReturn(
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+
+ mVolumeController.onVolumeChangedW(
+ AudioManager.STREAM_VOICE_CALL, AudioManager.FLAG_SHOW_UI);
+
+ verify(mCallback, never()).onStateChanged(any());
+ }
+
+ @Test
public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 61f7f30..f652cb0 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -371,8 +371,9 @@
return new LocationPermissionChecker(context);
}
- /** Gets the transports that need to be marked as restricted by the VCN */
- public Set<Integer> getRestrictedTransports(
+ /** Gets transports that need to be marked as restricted by the VCN from CarrierConfig */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Set<Integer> getRestrictedTransportsFromCarrierConfig(
ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot) {
if (!Build.IS_ENG && !Build.IS_USERDEBUG) {
return RESTRICTED_TRANSPORTS_DEFAULT;
@@ -398,6 +399,22 @@
}
return restrictedTransports;
}
+
+ /** Gets the transports that need to be marked as restricted by the VCN */
+ public Set<Integer> getRestrictedTransports(
+ ParcelUuid subGrp,
+ TelephonySubscriptionSnapshot lastSnapshot,
+ VcnConfig vcnConfig) {
+ final Set<Integer> restrictedTransports = new ArraySet<>();
+ restrictedTransports.addAll(vcnConfig.getRestrictedUnderlyingNetworkTransports());
+
+ // TODO: b/262269892 Remove the ability to configure restricted transports
+ // via CarrierConfig
+ restrictedTransports.addAll(
+ getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot));
+
+ return restrictedTransports;
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -719,6 +736,7 @@
if (mVcns.containsKey(subscriptionGroup)) {
final Vcn vcn = mVcns.get(subscriptionGroup);
vcn.updateConfig(config);
+ notifyAllPolicyListenersLocked();
} else {
// TODO(b/193687515): Support multiple VCNs active at the same time
if (isActiveSubGroup(subscriptionGroup, mLastSnapshot)) {
@@ -936,7 +954,6 @@
}
/** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */
- @GuardedBy("mLock")
@Override
public void addVcnUnderlyingNetworkPolicyListener(
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
@@ -963,16 +980,7 @@
});
}
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- void addVcnUnderlyingNetworkPolicyListenerForTest(
- @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
- synchronized (mLock) {
- addVcnUnderlyingNetworkPolicyListener(listener);
- }
- }
-
/** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
- @GuardedBy("mLock")
@Override
public void removeVcnUnderlyingNetworkPolicyListener(
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
@@ -1062,8 +1070,8 @@
isVcnManagedNetwork = true;
}
- final Set<Integer> restrictedTransports =
- mDeps.getRestrictedTransports(subGrp, mLastSnapshot);
+ final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports(
+ subGrp, mLastSnapshot, mConfigs.get(subGrp));
for (int restrictedTransport : restrictedTransports) {
if (ncCopy.hasTransport(restrictedTransport)) {
if (restrictedTransport == TRANSPORT_CELLULAR) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 430186a..6503029 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2797,7 +2797,11 @@
try {
pvr = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
- Slog.e(TAG, "Cannot setMode", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot setMode: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot setMode", e);
+ }
return;
}
@@ -3252,7 +3256,11 @@
try {
pvr = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) {
- Slog.e(TAG, "checkOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot checkOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot checkOperation", e);
+ }
return AppOpsManager.opToDefaultMode(code);
}
@@ -3458,7 +3466,11 @@
attributionTag = null;
}
} catch (SecurityException e) {
- Slog.e(TAG, "noteOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot noteOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot noteOperation", e);
+ }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3974,7 +3986,11 @@
attributionTag = null;
}
} catch (SecurityException e) {
- Slog.e(TAG, "startOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot startOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot startOperation", e);
+ }
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -4148,7 +4164,11 @@
attributionTag = null;
}
} catch (SecurityException e) {
- Slog.e(TAG, "Cannot finishOperation", e);
+ if (Process.isIsolated(uid)) {
+ Slog.e(TAG, "Cannot finishOperation: isolated process");
+ } else {
+ Slog.e(TAG, "Cannot finishOperation", e);
+ }
return;
}
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
index ab3b250..dce1c96 100644
--- a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -29,6 +29,7 @@
import java.io.EOFException;
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
+import java.net.ProtocolException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -39,12 +40,16 @@
// write contents of the host system's clipboard.
class EmulatorClipboardMonitor implements Consumer<ClipData> {
private static final String TAG = "EmulatorClipboardMonitor";
+
private static final String PIPE_NAME = "pipe:clipboard";
private static final int HOST_PORT = 5000;
- private final Thread mHostMonitorThread;
+
private static final boolean LOG_CLIBOARD_ACCESS =
SystemProperties.getBoolean("ro.boot.qemu.log_clipboard_access", false);
+ private static final int MAX_CLIPBOARD_BYTES = 128 << 20;
+
private FileDescriptor mPipe = null;
+ private final Thread mHostMonitorThread;
private static byte[] createOpenHandshake() {
// String.getBytes doesn't include the null terminator,
@@ -97,8 +102,8 @@
return fd;
}
- private static byte[] receiveMessage(final FileDescriptor fd) throws ErrnoException,
- InterruptedIOException, EOFException {
+ private byte[] receiveMessage(final FileDescriptor fd) throws ErrnoException,
+ InterruptedIOException, EOFException, ProtocolException {
final byte[] lengthBits = new byte[4];
readFully(fd, lengthBits, 0, lengthBits.length);
@@ -106,6 +111,10 @@
bb.order(ByteOrder.LITTLE_ENDIAN);
final int msgLen = bb.getInt();
+ if (msgLen <= 0 || msgLen > MAX_CLIPBOARD_BYTES) {
+ throw new ProtocolException("Clipboard message length: " + msgLen + " out of bounds.");
+ }
+
final byte[] msg = new byte[msgLen];
readFully(fd, msg, 0, msg.length);
@@ -150,7 +159,8 @@
}
setAndroidClipboard.accept(clip);
} catch (ErrnoException | EOFException | InterruptedIOException
- | InterruptedException e) {
+ | InterruptedException | ProtocolException | OutOfMemoryError e) {
+ Slog.w(TAG, "Failure to read from host clipboard", e);
setPipeFD(null);
try {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4fcde97..c15e419 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -140,6 +140,7 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
@@ -646,7 +647,10 @@
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.setTransportInfo(new VpnTransportInfo(
- VpnManager.TYPE_VPN_NONE, null /* sessionId */, false /* bypassable */))
+ VpnManager.TYPE_VPN_NONE,
+ null /* sessionId */,
+ false /* bypassable */,
+ false /* longLivedTcpConnectionsExpensive */))
.build();
loadAlwaysOnPackage();
@@ -711,7 +715,10 @@
mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
.setUids(null)
.setTransportInfo(new VpnTransportInfo(
- VpnManager.TYPE_VPN_NONE, null /* sessionId */, false /* bypassable */))
+ VpnManager.TYPE_VPN_NONE,
+ null /* sessionId */,
+ false /* bypassable */,
+ false /* longLivedTcpConnectionsExpensive */))
.build();
}
@@ -1570,7 +1577,8 @@
mConfig.allowedApplications, mConfig.disallowedApplications));
capsBuilder.setTransportInfo(
- new VpnTransportInfo(getActiveVpnType(), mConfig.session, mConfig.allowBypass));
+ new VpnTransportInfo(getActiveVpnType(), mConfig.session, mConfig.allowBypass,
+ false /* longLivedTcpConnectionsExpensive */));
// Only apps targeting Q and above can explicitly declare themselves as metered.
// These VPNs are assumed metered unless they state otherwise.
@@ -2776,6 +2784,16 @@
return hasIPV6 && !hasIPV4;
}
+ private void setVpnNetworkPreference(String session, Set<Range<Integer>> ranges) {
+ BinderUtils.withCleanCallingIdentity(
+ () -> mConnectivityManager.setVpnDefaultForUids(session, ranges));
+ }
+
+ private void clearVpnNetworkPreference(String session) {
+ BinderUtils.withCleanCallingIdentity(
+ () -> mConnectivityManager.setVpnDefaultForUids(session, Collections.EMPTY_LIST));
+ }
+
/**
* Internal class managing IKEv2/IPsec VPN connectivity
*
@@ -2887,6 +2905,9 @@
(r, exe) -> {
Log.d(TAG, "Runnable " + r + " rejected by the mExecutor");
});
+ setVpnNetworkPreference(mSessionKey,
+ createUserAndRestrictedProfilesRanges(mUserId,
+ mConfig.allowedApplications, mConfig.disallowedApplications));
}
@Override
@@ -3040,7 +3061,6 @@
mConfig.dnsServers.addAll(dnsAddrStrings);
mConfig.underlyingNetworks = new Network[] {network};
- mConfig.disallowedApplications = getAppExclusionList(mPackage);
networkAgent = mNetworkAgent;
@@ -3743,6 +3763,7 @@
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
mDiagnosticsCallback);
+ clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
}
@@ -4303,6 +4324,7 @@
mConfig.requiresInternetValidation = profile.requiresInternetValidation;
mConfig.excludeLocalRoutes = profile.excludeLocalRoutes;
mConfig.allowBypass = profile.isBypassable;
+ mConfig.disallowedApplications = getAppExclusionList(mPackage);
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
@@ -4455,6 +4477,9 @@
.setUids(createUserAndRestrictedProfilesRanges(
mUserId, null /* allowedApplications */, excludedApps))
.build();
+ setVpnNetworkPreference(getSessionKeyLocked(),
+ createUserAndRestrictedProfilesRanges(mUserId,
+ mConfig.allowedApplications, mConfig.disallowedApplications));
doSendNetworkCapabilities(mNetworkAgent, mNetworkCapabilities);
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index ad27c45..4a5b7e8 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -186,8 +186,8 @@
}
@Override
- public void setUpFsverity(String filePath, byte[] pkcs7Signature) throws IOException {
- VerityUtils.setUpFsverity(filePath, pkcs7Signature);
+ public void setUpFsverity(String filePath) throws IOException {
+ VerityUtils.setUpFsverity(filePath, /* signature */ (byte[]) null);
}
@Override
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 457d5b7..6f93608 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -78,7 +78,7 @@
interface FsverityUtil {
boolean isFromTrustedProvider(String path, byte[] pkcs7Signature);
- void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException;
+ void setUpFsverity(String path) throws IOException;
boolean rename(File src, File dest);
}
@@ -354,8 +354,7 @@
try {
// Do not parse font file before setting up fs-verity.
// setUpFsverity throws IOException if failed.
- mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
- pkcs7Signature);
+ mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath());
} catch (IOException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1e64701..89719ce 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1772,7 +1772,7 @@
private int runCompile() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- boolean checkProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+ boolean checkProfiles = true;
boolean forceCompilation = false;
boolean allPackages = false;
boolean clearProfileData = false;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index af507cd..50253ea 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -320,15 +320,13 @@
switch (profileType) {
case ArtManager.PROFILE_APPS :
- return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+ return true;
case ArtManager.PROFILE_BOOT_IMAGE:
// The device config property overrides the system property version.
boolean profileBootClassPath = SystemProperties.getBoolean(
"persist.device_config.runtime_native_boot.profilebootclasspath",
SystemProperties.getBoolean("dalvik.vm.profilebootclasspath", false));
- return (Build.IS_USERDEBUG || Build.IS_ENG) &&
- SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) &&
- profileBootClassPath;
+ return (Build.IS_USERDEBUG || Build.IS_ENG) && profileBootClassPath;
default:
throw new IllegalArgumentException("Invalid profile type:" + profileType);
}
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index 5cbe74c..a0e91ad 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -1,4 +1,5 @@
michaelwr@google.com
santoscordon@google.com
+philipjunker@google.com
per-file ThermalManagerService.java=wvw@google.com
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dbf05f1..ed4ba0d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4212,8 +4212,8 @@
}
private boolean forceSuspendInternal(int uid) {
- try {
- synchronized (mLock) {
+ synchronized (mLock) {
+ try {
mForceSuspendActive = true;
// Place the system in an non-interactive state
for (int idx = 0; idx < mPowerGroups.size(); idx++) {
@@ -4223,16 +4223,14 @@
// Disable all the partial wake locks as well
updateWakeLockDisabledStatesLocked();
- }
- Slog.i(TAG, "Force-Suspending (uid " + uid + ")...");
- boolean success = mNativeWrapper.nativeForceSuspend();
- if (!success) {
- Slog.i(TAG, "Force-Suspending failed in native.");
- }
- return success;
- } finally {
- synchronized (mLock) {
+ Slog.i(TAG, "Force-Suspending (uid " + uid + ")...");
+ boolean success = mNativeWrapper.nativeForceSuspend();
+ if (!success) {
+ Slog.i(TAG, "Force-Suspending failed in native.");
+ }
+ return success;
+ } finally {
mForceSuspendActive = false;
// Re-enable wake locks once again.
updateWakeLockDisabledStatesLocked();
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
new file mode 100644
index 0000000..868f34b
--- /dev/null
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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.security.rkp;
+
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.OutcomeReceiver;
+import android.security.rkp.IGetKeyCallback;
+import android.security.rkp.IRegistration;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
+import android.util.Log;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * Implements android.security.rkp.IRegistration as a thin wrapper around the java code
+ * exported by com.android.rkp.
+ *
+ * @hide
+ */
+final class RemoteProvisioningRegistration extends IRegistration.Stub {
+ static final String TAG = RemoteProvisioningService.TAG;
+ private final ConcurrentHashMap<IGetKeyCallback, CancellationSignal> mOperations =
+ new ConcurrentHashMap<>();
+ private final RegistrationProxy mRegistration;
+ private final Executor mExecutor;
+
+ private class GetKeyReceiver implements OutcomeReceiver<RemotelyProvisionedKey, Exception> {
+ IGetKeyCallback mCallback;
+ GetKeyReceiver(IGetKeyCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResult(RemotelyProvisionedKey result) {
+ mOperations.remove(mCallback);
+ Log.i(TAG, "Successfully fetched key for client " + mCallback.hashCode());
+ android.security.rkp.RemotelyProvisionedKey parcelable =
+ new android.security.rkp.RemotelyProvisionedKey();
+ parcelable.keyBlob = result.getKeyBlob();
+ parcelable.encodedCertChain = result.getEncodedCertChain();
+ wrapCallback(() -> mCallback.onSuccess(parcelable));
+ }
+
+ @Override
+ public void onError(Exception e) {
+ mOperations.remove(mCallback);
+ if (e instanceof OperationCanceledException) {
+ Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
+ wrapCallback(mCallback::onCancel);
+ } else {
+ Log.e(TAG, "Error fetching key for client " + mCallback.hashCode(), e);
+ wrapCallback(() -> mCallback.onError(e.getMessage()));
+ }
+ }
+ }
+
+ RemoteProvisioningRegistration(RegistrationProxy registration, Executor executor) {
+ mRegistration = registration;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void getKey(int keyId, IGetKeyCallback callback) {
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ if (mOperations.putIfAbsent(callback, cancellationSignal) != null) {
+ Log.e(TAG, "Client can only request one call at a time " + callback.hashCode());
+ throw new IllegalArgumentException(
+ "Callback is already associated with an existing operation: "
+ + callback.hashCode());
+ }
+
+ try {
+ Log.i(TAG, "Fetching key " + keyId + " for client " + callback.hashCode());
+ mRegistration.getKeyAsync(keyId, cancellationSignal, mExecutor,
+ new GetKeyReceiver(callback));
+ } catch (Exception e) {
+ Log.e(TAG, "getKeyAsync threw an exception for client " + callback.hashCode(), e);
+ mOperations.remove(callback);
+ wrapCallback(() -> callback.onError(e.getMessage()));
+ }
+ }
+
+ @Override
+ public void cancelGetKey(IGetKeyCallback callback) {
+ CancellationSignal cancellationSignal = mOperations.remove(callback);
+ if (cancellationSignal == null) {
+ throw new IllegalArgumentException(
+ "Invalid client in cancelGetKey: " + callback.hashCode());
+ }
+
+ Log.i(TAG, "Requesting cancellation for client " + callback.hashCode());
+ cancellationSignal.cancel();
+ }
+
+ @Override
+ public void storeUpgradedKey(byte[] oldKeyBlob, byte[] newKeyBlob) {
+ // TODO(b/262748535)
+ Log.e(TAG, "RegistrationBinder.storeUpgradedKey NOT YET IMPLEMENTED");
+ }
+
+ interface CallbackRunner {
+ void run() throws Exception;
+ }
+
+ private void wrapCallback(CallbackRunner callback) {
+ // Exceptions resulting from notifications to IGetKeyCallback objects can only be logged,
+ // since getKey execution is asynchronous, and there's no way for an exception to be
+ // properly handled up the stack.
+ try {
+ callback.run();
+ } catch (Exception e) {
+ Log.e(TAG, "Error invoking callback on client binder", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
index 65a4b38..cd1a968 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
@@ -20,9 +20,7 @@
import android.os.Binder;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
-import android.security.rkp.IGetKeyCallback;
import android.security.rkp.IGetRegistrationCallback;
-import android.security.rkp.IRegistration;
import android.security.rkp.IRemoteProvisioning;
import android.security.rkp.service.RegistrationProxy;
import android.util.Log;
@@ -30,6 +28,7 @@
import com.android.server.SystemService;
import java.time.Duration;
+import java.util.concurrent.Executor;
/**
* Implements the remote provisioning system service. This service is backed by a mainline
@@ -43,6 +42,35 @@
private static final Duration CREATE_REGISTRATION_TIMEOUT = Duration.ofSeconds(10);
private final RemoteProvisioningImpl mBinderImpl = new RemoteProvisioningImpl();
+ private static class RegistrationReceiver implements
+ OutcomeReceiver<RegistrationProxy, Exception> {
+ private final Executor mExecutor;
+ private final IGetRegistrationCallback mCallback;
+
+ RegistrationReceiver(Executor executor, IGetRegistrationCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResult(RegistrationProxy registration) {
+ try {
+ mCallback.onSuccess(new RemoteProvisioningRegistration(registration, mExecutor));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling success callback " + mCallback.hashCode(), e);
+ }
+ }
+
+ @Override
+ public void onError(Exception error) {
+ try {
+ mCallback.onError(error.toString());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling error callback " + mCallback.hashCode(), e);
+ }
+ }
+ }
+
/** @hide */
public RemoteProvisioningService(Context context) {
super(context);
@@ -54,73 +82,20 @@
}
private final class RemoteProvisioningImpl extends IRemoteProvisioning.Stub {
-
- final class RegistrationBinder extends IRegistration.Stub {
- static final String TAG = RemoteProvisioningService.TAG;
- private final RegistrationProxy mRegistration;
-
- RegistrationBinder(RegistrationProxy registration) {
- mRegistration = registration;
- }
-
- @Override
- public void getKey(int keyId, IGetKeyCallback callback) {
- Log.e(TAG, "RegistrationBinder.getKey NOT YET IMPLEMENTED");
- }
-
- @Override
- public void cancelGetKey(IGetKeyCallback callback) {
- Log.e(TAG, "RegistrationBinder.cancelGetKey NOT YET IMPLEMENTED");
- }
-
- @Override
- public void storeUpgradedKey(byte[] oldKeyBlob, byte[] newKeyBlob) {
- Log.e(TAG, "RegistrationBinder.storeUpgradedKey NOT YET IMPLEMENTED");
- }
- }
-
@Override
public void getRegistration(String irpcName, IGetRegistrationCallback callback)
throws RemoteException {
final int callerUid = Binder.getCallingUidOrThrow();
final long callingIdentity = Binder.clearCallingIdentity();
+ final Executor executor = getContext().getMainExecutor();
try {
Log.i(TAG, "getRegistration(" + irpcName + ")");
- RegistrationProxy.createAsync(
- getContext(),
- callerUid,
- irpcName,
- CREATE_REGISTRATION_TIMEOUT,
- getContext().getMainExecutor(),
- new OutcomeReceiver<>() {
- @Override
- public void onResult(RegistrationProxy registration) {
- try {
- callback.onSuccess(new RegistrationBinder(registration));
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling success callback", e);
- }
- }
-
- @Override
- public void onError(Exception error) {
- try {
- callback.onError(error.toString());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling error callback", e);
- }
- }
- });
+ RegistrationProxy.createAsync(getContext(), callerUid, irpcName,
+ CREATE_REGISTRATION_TIMEOUT, executor,
+ new RegistrationReceiver(executor, callback));
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
}
-
- @Override
- public void cancelGetRegistration(IGetRegistrationCallback callback)
- throws RemoteException {
- Log.i(TAG, "cancelGetRegistration()");
- callback.onError("cancelGetRegistration not yet implemented");
- }
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 05df22f..739aff7 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -26,16 +26,20 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.VcnManagementService.VDBG;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
@@ -50,11 +54,13 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkRequest;
import android.net.NetworkScore;
import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
import android.net.Uri;
import android.net.annotations.PolicyDirection;
+import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.ipsec.ike.ChildSessionParams;
@@ -63,11 +69,14 @@
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnManager;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.os.Handler;
@@ -165,6 +174,9 @@
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ /** Default number of parallel SAs requested */
+ static final int TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT = 1;
+
// Matches DataConnection.NETWORK_TYPE private constant, and magic string from
// ConnectivityManager#getNetworkTypeName()
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -546,6 +558,39 @@
}
}
+ /**
+ * Sent when there is a suspected data stall on a network
+ *
+ * <p>Only relevant in the Connected state.
+ *
+ * @param arg1 The "all" token; this signal is always honored.
+ * @param obj @NonNull An EventDataStallSuspectedInfo instance with relevant data.
+ */
+ private static final int EVENT_DATA_STALL_SUSPECTED = 13;
+
+ private static class EventDataStallSuspectedInfo implements EventInfo {
+ @NonNull public final Network network;
+
+ EventDataStallSuspectedInfo(@NonNull Network network) {
+ this.network = network;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof EventDataStallSuspectedInfo)) {
+ return false;
+ }
+
+ final EventDataStallSuspectedInfo rhs = (EventDataStallSuspectedInfo) other;
+ return Objects.equals(network, rhs.network);
+ }
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -578,10 +623,13 @@
@NonNull
private final VcnUnderlyingNetworkControllerCallback mUnderlyingNetworkControllerCallback;
+ @NonNull private final VcnConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
+
private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager;
@NonNull private final ConnectivityManager mConnectivityManager;
+ @NonNull private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
@Nullable private IpSecTunnelInterface mTunnelIface = null;
@@ -748,6 +796,20 @@
mUnderlyingNetworkControllerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+ mConnectivityDiagnosticsManager =
+ mVcnContext.getContext().getSystemService(ConnectivityDiagnosticsManager.class);
+
+ mConnectivityDiagnosticsCallback = new VcnConnectivityDiagnosticsCallback();
+
+ if (mConnectionConfig.hasGatewayOption(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)) {
+ final NetworkRequest diagRequest =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+ mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
+ diagRequest,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ mConnectivityDiagnosticsCallback);
+ }
addState(mDisconnectedState);
addState(mDisconnectingState);
@@ -810,6 +872,9 @@
mUnderlyingNetworkController.teardown();
mGatewayStatusCallback.onQuit();
+
+ mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback);
}
/**
@@ -828,6 +893,20 @@
sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
+ private class VcnConnectivityDiagnosticsCallback extends ConnectivityDiagnosticsCallback {
+ @Override
+ public void onDataStallSuspected(ConnectivityDiagnosticsManager.DataStallReport report) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ final Network network = report.getNetwork();
+ logInfo("Data stall suspected on " + network);
+ sendMessageAndAcquireWakeLock(
+ EVENT_DATA_STALL_SUSPECTED,
+ TOKEN_ALL,
+ new EventDataStallSuspectedInfo(network));
+ }
+ }
+
private class VcnUnderlyingNetworkControllerCallback
implements UnderlyingNetworkControllerCallback {
@Override
@@ -1367,7 +1446,8 @@
case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
case EVENT_MIGRATION_COMPLETED: // Fallthrough
- case EVENT_IKE_CONNECTION_INFO_CHANGED:
+ case EVENT_IKE_CONNECTION_INFO_CHANGED: // Fallthrough
+ case EVENT_DATA_STALL_SUSPECTED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -1908,6 +1988,22 @@
mChildConfig,
oldChildConfig,
mIkeConnectionInfo);
+
+ // Create opportunistic child SAs; this allows SA aggregation in the downlink,
+ // reducing lock/atomic contention in high throughput scenarios. All SAs will
+ // share the same UDP encap socket (and keepalives) as necessary, and are
+ // effectively free.
+ final int parallelTunnelCount =
+ mDeps.getParallelTunnelCount(mLastSnapshot, mSubscriptionGroup);
+ logInfo("Parallel tunnel count: " + parallelTunnelCount);
+
+ for (int i = 0; i < parallelTunnelCount - 1; i++) {
+ mIkeSession.openChildSession(
+ buildOpportunisticChildParams(),
+ new VcnChildSessionCallback(
+ mCurrentToken, true /* isOpportunistic */));
+ }
+
break;
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
@@ -1925,6 +2021,11 @@
mIkeConnectionInfo =
((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo;
break;
+ case EVENT_DATA_STALL_SUSPECTED:
+ final Network networkWithDataStall =
+ ((EventDataStallSuspectedInfo) msg.obj).network;
+ handleDataStallSuspected(networkWithDataStall);
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -1985,6 +2086,15 @@
}
}
+ private void handleDataStallSuspected(Network networkWithDataStall) {
+ if (mUnderlying != null
+ && mNetworkAgent != null
+ && mNetworkAgent.getNetwork().equals(networkWithDataStall)) {
+ logInfo("Perform Mobility update to recover from suspected data stall");
+ mIkeSession.setNetwork(mUnderlying.network);
+ }
+ }
+
protected void setupInterfaceAndNetworkAgent(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
@@ -2264,15 +2374,44 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
public class VcnChildSessionCallback implements ChildSessionCallback {
private final int mToken;
+ private final boolean mIsOpportunistic;
+
+ private boolean mIsChildOpened = false;
VcnChildSessionCallback(int token) {
+ this(token, false /* isOpportunistic */);
+ }
+
+ /**
+ * Creates a ChildSessionCallback
+ *
+ * <p>If configured as opportunistic, transforms will not report initial startup, or
+ * associated startup failures. This serves the dual purposes of ensuring that if the server
+ * does not support connection multiplexing, new child SA negotiations will be ignored, and
+ * at the same time, will notify the VCN session if a successfully negotiated opportunistic
+ * child SA is subsequently torn down, which could impact uplink traffic if the SA in use
+ * for outbound/uplink traffic is this opportunistic SA.
+ *
+ * <p>While inbound SAs can be used in parallel, the IPsec stack explicitly selects the last
+ * applied outbound transform for outbound traffic. This means that unlike inbound traffic,
+ * outbound does not benefit from these parallel SAs in the same manner.
+ */
+ VcnChildSessionCallback(int token, boolean isOpportunistic) {
mToken = token;
+ mIsOpportunistic = isOpportunistic;
}
/** Internal proxy method for injecting of mocked ChildSessionConfiguration */
@VisibleForTesting(visibility = Visibility.PRIVATE)
void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
logDbg("ChildOpened for token " + mToken);
+
+ if (mIsOpportunistic) {
+ logDbg("ChildOpened for opportunistic child; suppressing event message");
+ mIsChildOpened = true;
+ return;
+ }
+
childOpened(mToken, childConfig);
}
@@ -2284,12 +2423,24 @@
@Override
public void onClosed() {
logDbg("ChildClosed for token " + mToken);
+
+ if (mIsOpportunistic && !mIsChildOpened) {
+ logDbg("ChildClosed for unopened opportunistic child; ignoring");
+ return;
+ }
+
sessionLost(mToken, null);
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
logInfo("ChildClosedExceptionally for token " + mToken, exception);
+
+ if (mIsOpportunistic && !mIsChildOpened) {
+ logInfo("ChildClosedExceptionally for unopened opportunistic child; ignoring");
+ return;
+ }
+
sessionLost(mToken, exception);
}
@@ -2424,6 +2575,11 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ ConnectivityDiagnosticsCallback getConnectivityDiagnosticsCallback() {
+ return mConnectivityDiagnosticsCallback;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkRecord getUnderlyingNetwork() {
return mUnderlying;
}
@@ -2489,6 +2645,30 @@
return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
}
+ private ChildSessionParams buildOpportunisticChildParams() {
+ final ChildSessionParams baseParams =
+ mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
+
+ final TunnelModeChildSessionParams.Builder builder =
+ new TunnelModeChildSessionParams.Builder();
+ for (ChildSaProposal proposal : baseParams.getChildSaProposals()) {
+ builder.addChildSaProposal(proposal);
+ }
+
+ for (IkeTrafficSelector inboundSelector : baseParams.getInboundTrafficSelectors()) {
+ builder.addInboundTrafficSelectors(inboundSelector);
+ }
+
+ for (IkeTrafficSelector outboundSelector : baseParams.getOutboundTrafficSelectors()) {
+ builder.addOutboundTrafficSelectors(outboundSelector);
+ }
+
+ builder.setLifetimeSeconds(
+ baseParams.getHardLifetimeSeconds(), baseParams.getSoftLifetimeSeconds());
+
+ return builder.build();
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnIkeSession buildIkeSession(@NonNull Network network) {
final int token = ++mCurrentToken;
@@ -2589,6 +2769,23 @@
return 0;
}
}
+
+ /** Gets the max number of parallel tunnels allowed for tunnel aggregation. */
+ public int getParallelTunnelCount(
+ TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) {
+ PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp);
+ int result = TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT;
+
+ if (carrierConfig != null) {
+ result =
+ carrierConfig.getInt(
+ VcnManager.VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
+ TUNNEL_AGGREGATION_SA_COUNT_MAX_DEFAULT);
+ }
+
+ // Guard against tunnel count < 1
+ return Math.max(1, result);
+ }
}
/**
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 2f84fdd..2141eba 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -15,6 +15,7 @@
*/
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,6 +46,7 @@
import com.android.server.vcn.VcnContext;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -69,9 +71,23 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
- /** Priority for any other networks (including unvalidated, etc) */
+ /**
+ * Priority for networks that VCN can fall back to.
+ *
+ * <p>If none of the network candidates are validated or match any template, VCN will fall back
+ * to any INTERNET network.
+ */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- static final int PRIORITY_ANY = Integer.MAX_VALUE;
+ static final int PRIORITY_FALLBACK = Integer.MAX_VALUE;
+
+ /**
+ * Priority for networks that cannot be selected as VCN's underlying networks.
+ *
+ * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any
+ * template as the underlying network.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int PRIORITY_INVALID = -1;
/** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
public static int calculatePriorityClass(
@@ -86,12 +102,12 @@
if (networkRecord.isBlocked) {
logWtf("Network blocked for System Server: " + networkRecord.network);
- return PRIORITY_ANY;
+ return PRIORITY_INVALID;
}
if (snapshot == null) {
logWtf("Got null snapshot");
- return PRIORITY_ANY;
+ return PRIORITY_INVALID;
}
int priorityIndex = 0;
@@ -108,7 +124,13 @@
}
priorityIndex++;
}
- return PRIORITY_ANY;
+
+ final NetworkCapabilities caps = networkRecord.networkCapabilities;
+ if (caps.hasCapability(NET_CAPABILITY_INTERNET)
+ || (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST))) {
+ return PRIORITY_FALLBACK;
+ }
+ return PRIORITY_INVALID;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -297,6 +319,18 @@
return false;
}
+ for (Map.Entry<Integer, Integer> entry :
+ networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
+ final int cap = entry.getKey();
+ final int matchCriteria = entry.getValue();
+
+ if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
+ return false;
+ } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
+ return false;
+ }
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index d474c5d..6afa795 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -16,6 +16,9 @@
package com.android.server.vcn.routeselection;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -32,6 +35,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
@@ -40,6 +44,7 @@
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +54,7 @@
import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -126,6 +132,63 @@
registerOrUpdateNetworkRequests();
}
+ private static class CapabilityMatchCriteria {
+ public final int capability;
+ public final int matchCriteria;
+
+ CapabilityMatchCriteria(int capability, int matchCriteria) {
+ this.capability = capability;
+ this.matchCriteria = matchCriteria;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(capability, matchCriteria);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof CapabilityMatchCriteria)) {
+ return false;
+ }
+
+ final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other;
+ return capability == rhs.capability && matchCriteria == rhs.matchCriteria;
+ }
+ }
+
+ private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell(
+ VcnGatewayConnectionConfig connectionConfig) {
+ final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>();
+
+ for (VcnUnderlyingNetworkTemplate template :
+ connectionConfig.getVcnUnderlyingNetworkPriorities()) {
+ if (template instanceof VcnCellUnderlyingNetworkTemplate) {
+ final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>();
+
+ for (Map.Entry<Integer, Integer> entry :
+ ((VcnCellUnderlyingNetworkTemplate) template)
+ .getCapabilitiesMatchCriteria()
+ .entrySet()) {
+
+ final int capability = entry.getKey();
+ final int matchCriteria = entry.getValue();
+ if (matchCriteria != MATCH_ANY) {
+ capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria));
+ }
+ }
+
+ dedupedCapsMatchSets.add(capsMatchSet);
+ }
+ }
+
+ dedupedCapsMatchSets.add(
+ Collections.singleton(
+ new CapabilityMatchCriteria(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED)));
+ return dedupedCapsMatchSets;
+ }
+
private void registerOrUpdateNetworkRequests() {
NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
NetworkCallback oldWifiCallback = mWifiBringupCallback;
@@ -158,11 +221,14 @@
getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
- final NetworkBringupCallback cb = new NetworkBringupCallback();
- mCellBringupCallbacks.add(cb);
+ for (Set<CapabilityMatchCriteria> capsMatchCriteria :
+ dedupAndGetCapRequirementsForCell(mConnectionConfig)) {
+ final NetworkBringupCallback cb = new NetworkBringupCallback();
+ mCellBringupCallbacks.add(cb);
- mConnectivityManager.requestBackgroundNetwork(
- getCellNetworkRequestForSubId(subId), cb, mHandler);
+ mConnectivityManager.requestBackgroundNetwork(
+ getCellNetworkRequestForSubId(subId, capsMatchCriteria), cb, mHandler);
+ }
}
} else {
mRouteSelectionCallback = null;
@@ -214,6 +280,13 @@
.build();
}
+ private NetworkRequest.Builder getBaseWifiNetworkRequestBuilder() {
+ return getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
+ }
+
/**
* Builds the WiFi bringup request
*
@@ -224,10 +297,7 @@
* but will NEVER bring up a Carrier WiFi network itself.
*/
private NetworkRequest getWifiNetworkRequest() {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
- .build();
+ return getBaseWifiNetworkRequestBuilder().build();
}
/**
@@ -238,9 +308,7 @@
* pace to effectively select a short-lived WiFi offload network.
*/
private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ return getBaseWifiNetworkRequestBuilder()
// Ensure wifi updates signal strengths when crossing this threshold.
.setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
.build();
@@ -254,9 +322,7 @@
* pace to effectively select away from a failing WiFi network.
*/
private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ return getBaseWifiNetworkRequestBuilder()
// Ensure wifi updates signal strengths when crossing this threshold.
.setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
.build();
@@ -273,11 +339,25 @@
* <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified
* in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier.
*/
- private NetworkRequest getCellNetworkRequestForSubId(int subId) {
- return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
- .build();
+ private NetworkRequest getCellNetworkRequestForSubId(
+ int subId, Set<CapabilityMatchCriteria> capsMatchCriteria) {
+ final NetworkRequest.Builder nrBuilder =
+ getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+
+ for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) {
+ final int cap = capMatchCriteria.capability;
+ final int matchCriteria = capMatchCriteria.matchCriteria;
+
+ if (matchCriteria == MATCH_REQUIRED) {
+ nrBuilder.addCapability(cap);
+ } else if (matchCriteria == MATCH_FORBIDDEN) {
+ nrBuilder.addForbiddenCapability(cap);
+ }
+ }
+
+ return nrBuilder.build();
}
/**
@@ -285,7 +365,6 @@
*/
private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
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);
@@ -356,7 +435,7 @@
if (!allNetworkPriorities.isEmpty()) {
allNetworkPriorities += ", ";
}
- allNetworkPriorities += record.network + ": " + record.getPriorityClass();
+ allNetworkPriorities += record.network + ": " + record.priorityClass;
}
logInfo(
"Selected network changed to "
@@ -393,19 +472,22 @@
private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() {
TreeSet<UnderlyingNetworkRecord> sorted =
- new TreeSet<>(
- UnderlyingNetworkRecord.getComparator(
+ new TreeSet<>(UnderlyingNetworkRecord.getComparator());
+
+ for (UnderlyingNetworkRecord.Builder builder :
+ mUnderlyingNetworkRecordBuilders.values()) {
+ if (builder.isValid()) {
+ final UnderlyingNetworkRecord record =
+ builder.build(
mVcnContext,
mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
mSubscriptionGroup,
mLastSnapshot,
mCurrentRecord,
- mCarrierConfig));
-
- for (UnderlyingNetworkRecord.Builder builder :
- mUnderlyingNetworkRecordBuilders.values()) {
- if (builder.isValid()) {
- sorted.add(builder.build());
+ mCarrierConfig);
+ if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
+ sorted.add(record);
+ }
}
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index 319680e..aea9f4d 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -42,53 +42,58 @@
* @hide
*/
public class UnderlyingNetworkRecord {
- private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE;
-
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
public final boolean isBlocked;
-
- private int mPriorityClass = PRIORITY_CLASS_INVALID;
+ public final boolean isSelected;
+ public final int priorityClass;
@VisibleForTesting(visibility = Visibility.PRIVATE)
public UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
- boolean isBlocked) {
- this.network = network;
- this.networkCapabilities = networkCapabilities;
- this.linkProperties = linkProperties;
- this.isBlocked = isBlocked;
- }
-
- private int getOrCalculatePriorityClass(
+ boolean isBlocked,
VcnContext vcnContext,
List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
PersistableBundleWrapper carrierConfig) {
- // Never changes after the underlying network record is created.
- if (mPriorityClass == PRIORITY_CLASS_INVALID) {
- mPriorityClass =
- NetworkPriorityClassifier.calculatePriorityClass(
- vcnContext,
- this,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
- }
+ this.network = network;
+ this.networkCapabilities = networkCapabilities;
+ this.linkProperties = linkProperties;
+ this.isBlocked = isBlocked;
- return mPriorityClass;
+ this.isSelected = isSelected(this.network, currentlySelected);
+
+ priorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ vcnContext,
+ this,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
}
- // Used in UnderlyingNetworkController
- int getPriorityClass() {
- return mPriorityClass;
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public UnderlyingNetworkRecord(
+ @NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties,
+ boolean isBlocked,
+ boolean isSelected,
+ int priorityClass) {
+ this.network = network;
+ this.networkCapabilities = networkCapabilities;
+ this.linkProperties = linkProperties;
+ this.isBlocked = isBlocked;
+ this.isSelected = isSelected;
+
+ this.priorityClass = priorityClass;
}
@Override
@@ -108,40 +113,32 @@
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
}
- static Comparator<UnderlyingNetworkRecord> getComparator(
- VcnContext vcnContext,
- List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundleWrapper carrierConfig) {
+ /** Returns if two records are equal including their priority classes. */
+ public static boolean isEqualIncludingPriorities(
+ UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) {
+ if (left != null && right != null) {
+ return left.equals(right)
+ && left.isSelected == right.isSelected
+ && left.priorityClass == right.priorityClass;
+ }
+
+ return left == right;
+ }
+
+ static Comparator<UnderlyingNetworkRecord> getComparator() {
return (left, right) -> {
- final int leftIndex =
- left.getOrCalculatePriorityClass(
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
- final int rightIndex =
- right.getOrCalculatePriorityClass(
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
+ final int leftIndex = left.priorityClass;
+ final int rightIndex = right.priorityClass;
// In the case of networks in the same priority class, prioritize based on other
// criteria (eg. actively selected network, link metrics, etc)
if (leftIndex == rightIndex) {
// TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
// fall into the same priority class.
- if (isSelected(left, currentlySelected)) {
+ if (left.isSelected) {
return -1;
}
- if (isSelected(left, currentlySelected)) {
+ if (right.isSelected) {
return 1;
}
}
@@ -150,11 +147,11 @@
}
private static boolean isSelected(
- UnderlyingNetworkRecord recordToCheck, UnderlyingNetworkRecord currentlySelected) {
+ Network networkToCheck, UnderlyingNetworkRecord currentlySelected) {
if (currentlySelected == null) {
return false;
}
- if (currentlySelected.network == recordToCheck.network) {
+ if (currentlySelected.network.equals(networkToCheck)) {
return true;
}
return false;
@@ -172,16 +169,8 @@
pw.println("UnderlyingNetworkRecord:");
pw.increaseIndent();
- final int priorityIndex =
- getOrCalculatePriorityClass(
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
-
- pw.println("Priority index: " + priorityIndex);
+ pw.println("priorityClass: " + priorityClass);
+ pw.println("isSelected: " + isSelected);
pw.println("mNetwork: " + network);
pw.println("mNetworkCapabilities: " + networkCapabilities);
pw.println("mLinkProperties: " + linkProperties);
@@ -198,8 +187,6 @@
boolean mIsBlocked;
boolean mWasIsBlockedSet;
- @Nullable private UnderlyingNetworkRecord mCached;
-
Builder(@NonNull Network network) {
mNetwork = network;
}
@@ -211,7 +198,6 @@
void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
mNetworkCapabilities = networkCapabilities;
- mCached = null;
}
@Nullable
@@ -221,32 +207,40 @@
void setLinkProperties(@NonNull LinkProperties linkProperties) {
mLinkProperties = linkProperties;
- mCached = null;
}
void setIsBlocked(boolean isBlocked) {
mIsBlocked = isBlocked;
mWasIsBlockedSet = true;
- mCached = null;
}
boolean isValid() {
return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
}
- UnderlyingNetworkRecord build() {
+ UnderlyingNetworkRecord build(
+ VcnContext vcnContext,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundleWrapper carrierConfig) {
if (!isValid()) {
throw new IllegalArgumentException(
"Called build before UnderlyingNetworkRecord was valid");
}
- if (mCached == null) {
- mCached =
- new UnderlyingNetworkRecord(
- mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
- }
-
- return mCached;
+ return new UnderlyingNetworkRecord(
+ mNetwork,
+ mNetworkCapabilities,
+ mLinkProperties,
+ mIsBlocked,
+ vcnContext,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index d22ec0a..d6761a2 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -573,5 +573,10 @@
return isEqual(mBundle, other.mBundle);
}
+
+ @Override
+ public String toString() {
+ return mBundle.toString();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ff09163..330c098 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1085,10 +1085,14 @@
// Use launch-adjacent-flag-root if launching with launch-adjacent flag.
if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
&& mLaunchAdjacentFlagRootTask != null) {
- // If the adjacent launch is coming from the same root, launch to adjacent root instead.
- if (sourceTask != null && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
+ if (sourceTask != null && sourceTask == candidateTask) {
+ // Do nothing when task that is getting opened is same as the source.
+ } else if (sourceTask != null
+ && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
&& (sourceTask == mLaunchAdjacentFlagRootTask
|| sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
+ // If the adjacent launch is coming from the same root, launch to
+ // adjacent root instead.
return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
} else {
return mLaunchAdjacentFlagRootTask;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 202fe55..2b15a79 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -63,6 +63,7 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -261,7 +262,7 @@
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
- if (isSysUiPackage || mUid == Process.SYSTEM_UID) {
+ if (isSysUiPackage || UserHandle.getAppId(mUid) == Process.SYSTEM_UID) {
// This is a system owned process and should not use an activity config.
// TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs.
mIsActivityConfigOverrideAllowed = false;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 8e0dc5d..075991e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -183,7 +183,7 @@
"android.frameworks.sensorservice@1.0",
"android.frameworks.sensorservice-V1-ndk",
"android.frameworks.stats@1.0",
- "android.frameworks.stats-V1-ndk",
+ "android.frameworks.stats-V2-ndk",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend-V1-ndk",
diff --git a/services/proguard.flags b/services/proguard.flags
index eb5c714..4c2830e 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -72,6 +72,13 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.wm.** implements com.android.server.wm.DisplayAreaPolicy$Provider
# JNI keep rules
+# The global keep rule for native methods allows stripping of such methods if they're unreferenced
+# in Java. However, because system_server explicitly registers these methods from native code,
+# stripping them in Java can cause runtime issues. As such, conservatively keep all such methods in
+# system_server subpackages as long as the containing class is also kept or referenced.
+-keepclassmembers class com.android.server.** {
+ native <methods>;
+}
# TODO(b/210510433): Revisit and fix with @Keep, or consider auto-generating from
# frameworks/base/services/core/jni/onload.cpp.
-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.BroadcastRadioService { *; }
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 9672085..68e5ebf 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -109,17 +109,16 @@
@Override
public boolean isFromTrustedProvider(String path, byte[] signature) {
- return mHasFsverityPaths.contains(path);
+ if (!mHasFsverityPaths.contains(path)) {
+ return false;
+ }
+ String fakeSignature = new String(signature, StandardCharsets.UTF_8);
+ return GOOD_SIGNATURE.equals(fakeSignature);
}
@Override
- public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
- String fakeSignature = new String(pkcs7Signature, StandardCharsets.UTF_8);
- if (GOOD_SIGNATURE.equals(fakeSignature)) {
- mHasFsverityPaths.add(path);
- } else {
- throw new IOException("Failed to set up fake fs-verity");
- }
+ public void setUpFsverity(String path) throws IOException {
+ mHasFsverityPaths.add(path);
}
@Override
@@ -813,8 +812,8 @@
}
@Override
- public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
- mFakeFsverityUtil.setUpFsverity(path, pkcs7Signature);
+ public void setUpFsverity(String path) throws IOException {
+ mFakeFsverityUtil.setUpFsverity(path);
}
@Override
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 0fdf40d..27ba676 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -605,9 +605,9 @@
EUTRAN_ARFCN_FREQUENCY_BAND_41(
EutranBand.BAND_41, 2496000, 39650, 41589, 2496000, 39650, 41589),
EUTRAN_ARFCN_FREQUENCY_BAND_42(
- EutranBand.BAND_42, 3400000, 41950, 43589, 3400000, 41950, 43589),
+ EutranBand.BAND_42, 3400000, 41590, 43589, 3400000, 41590, 43589),
EUTRAN_ARFCN_FREQUENCY_BAND_43(
- EutranBand.BAND_43, 3600000, 43950, 45589, 3600000, 43950, 45589),
+ EutranBand.BAND_43, 3600000, 43590, 45589, 3600000, 43590, 45589),
EUTRAN_ARFCN_FREQUENCY_BAND_44(
EutranBand.BAND_44, 703000, 45590, 46589, 703000, 45590, 46589),
EUTRAN_ARFCN_FREQUENCY_BAND_45(
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 87f45010..d8c1b57e3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -39,7 +39,6 @@
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
import android.telephony.gba.TlsParams;
import android.telephony.gba.UaSecurityProtocolIdentifier;
import android.telephony.ims.ImsReasonInfo;
@@ -1137,27 +1136,6 @@
public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
/**
- * The data call retry configuration for different types of APN.
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS =
- "carrier_data_call_retry_config_strings";
-
- /**
- * Delay in milliseconds between trying APN from the pool
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG =
- "carrier_data_call_apn_delay_default_long";
-
- /**
- * Faster delay in milliseconds between trying APN from the pool
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG =
- "carrier_data_call_apn_delay_faster_long";
-
- /**
* Delay in milliseconds for retrying APN after disconnect
* @hide
*/
@@ -1165,25 +1143,7 @@
"carrier_data_call_apn_retry_after_disconnect_long";
/**
- * The maximum times for telephony to retry data setup on the same APN requested by
- * network through the data setup response retry timer
- * {@link DataCallResponse#getRetryDurationMillis()}. This is to prevent that network keeps
- * asking device to retry data setup forever and causes power consumption issue. For infinite
- * retring same APN, configure this as 2147483647 (i.e. {@link Integer#MAX_VALUE}).
- *
- * Note if network does not suggest any retry timer, frameworks uses the retry configuration
- * from {@link #KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS}, and the maximum retry times could
- * be configured there.
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT =
- "carrier_data_call_retry_network_requested_max_count_int";
-
- /**
- * Data call setup permanent failure causes by the carrier.
- *
- * @deprecated This API key was added in mistake and is not used anymore by the telephony data
- * frameworks.
+ * Data call setup permanent failure causes by the carrier
*/
@Deprecated
public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
@@ -1203,19 +1163,6 @@
"carrier_metered_roaming_apn_types_strings";
/**
- * APN types that are not allowed on cellular
- * @hide
- */
- public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
- "carrier_wwan_disallowed_apn_types_string_array";
-
- /**
- * APN types that are not allowed on IWLAN
- * @hide
- */
- public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
- "carrier_wlan_disallowed_apn_types_string_array";
- /**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
@@ -4361,6 +4308,18 @@
"data_switch_validation_timeout_long";
/**
+ * The minimum timeout of UDP port 4500 NAT / firewall entries on the Internet PDN of this
+ * carrier network. This will be used by Android platform VPNs to tune IPsec NAT keepalive
+ * interval. If this value is too low to provide uninterrupted inbound connectivity, then
+ * Android system VPNs may indicate to applications that the VPN cannot support long-lived
+ * TCP connections.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final String KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT =
+ "min_udp_port_4500_nat_timeout_sec_int";
+
+ /**
* Specifies whether the system should prefix the EAP method to the anonymous identity.
* The following prefix will be added if this key is set to TRUE:
* EAP-AKA: "0"
@@ -8061,6 +8020,14 @@
public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL =
KEY_PREFIX + "supports_eap_aka_fast_reauth_bool";
+ /**
+ * Type of IP preference used to prioritize ePDG servers. Possible values are
+ * {@link #EPDG_ADDRESS_IPV4_PREFERRED}, {@link #EPDG_ADDRESS_IPV6_PREFERRED},
+ * {@link #EPDG_ADDRESS_IPV4_ONLY}
+ */
+ public static final String KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT =
+ KEY_PREFIX + "epdg_address_ip_type_preference_int";
+
/** @hide */
@IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
public @interface AuthenticationMethodType {}
@@ -8125,6 +8092,39 @@
*/
public static final int ID_TYPE_KEY_ID = 11;
+ /** @hide */
+ @IntDef({
+ EPDG_ADDRESS_IPV4_PREFERRED,
+ EPDG_ADDRESS_IPV6_PREFERRED,
+ EPDG_ADDRESS_IPV4_ONLY,
+ EPDG_ADDRESS_IPV6_ONLY,
+ EPDG_ADDRESS_SYSTEM_PREFERRED
+ })
+ public @interface EpdgAddressIpPreference {}
+
+ /** Prioritize IPv4 ePDG addresses. */
+ public static final int EPDG_ADDRESS_IPV4_PREFERRED = 0;
+
+ /** Prioritize IPv6 ePDG addresses */
+ public static final int EPDG_ADDRESS_IPV6_PREFERRED = 1;
+
+ /** Use IPv4 ePDG addresses only. */
+ public static final int EPDG_ADDRESS_IPV4_ONLY = 2;
+
+ /** Use IPv6 ePDG addresses only.
+ * @hide
+ */
+ public static final int EPDG_ADDRESS_IPV6_ONLY = 3;
+
+ /** Follow the priority from DNS resolution results, which are sorted by using RFC6724
+ * algorithm.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6724#section-6">RFC 6724, Default Address
+ * Selection for Internet Protocol Version 6 (IPv6)</a>
+ * @hide
+ */
+ public static final int EPDG_ADDRESS_SYSTEM_PREFERRED = 4;
+
private Iwlan() {}
private static PersistableBundle getDefaults() {
@@ -8208,7 +8208,7 @@
defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
defaults.putBoolean(KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL, false);
-
+ defaults.putInt(KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT, EPDG_ADDRESS_IPV4_PREFERRED);
return defaults;
}
}
@@ -8349,7 +8349,6 @@
* "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
* 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
*
- * // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS
* @hide
*/
public static final String KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY =
@@ -8756,27 +8755,13 @@
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
- sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
- "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
- + "320000:5000,640000:5000,1280000:5000,1800000:5000",
- "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
- + "320000:5000,640000:5000,1280000:5000,1800000:5000",
- "ims:max_retries=10, 5000, 5000, 5000",
- "others:max_retries=3, 5000, 5000, 5000"});
- sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
- sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 3000);
- sDefaults.putInt(KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, 3);
sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
- sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
- new String[]{""});
- sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
- new String[]{""});
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
@@ -9198,6 +9183,7 @@
sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
CellSignalStrengthLte.USE_RSRP);
+ sDefaults.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, 300);
// Default wifi configurations.
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index f794a79..3379ce5 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1284,7 +1284,7 @@
&& xorEqualsInt(this.mMmsProxyPort, other.mMmsProxyPort))
&& xorEqualsString(this.mUser, other.mUser)
&& xorEqualsString(this.mPassword, other.mPassword)
- && xorEqualsInt(this.mAuthType, other.mAuthType)
+ && Objects.equals(this.mAuthType, other.mAuthType)
&& !typeSameAny(this, other)
&& Objects.equals(this.mOperatorNumeric, other.mOperatorNumeric)
&& Objects.equals(this.mProtocol, other.mProtocol)
diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
index 0ab7b61..a0d9c1bd 100644
--- a/telephony/java/android/telephony/data/QosBearerFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -130,6 +130,10 @@
return precedence;
}
+ public int getProtocol() {
+ return protocol;
+ }
+
public static class PortRange implements Parcelable {
int start;
int end;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0ce6b14..da1ffcd 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2510,9 +2510,6 @@
CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
String callingFeatureId);
- /** Check if telephony new data stack is enabled. */
- boolean isUsingNewDataStack();
-
/**
* @return true if the modem service is set successfully, false otherwise.
*/
diff --git a/tests/SurfaceControlViewHostTest/OWNERS b/tests/SurfaceControlViewHostTest/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/tests/SurfaceControlViewHostTest/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 2fbcf9d..1569613 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -20,6 +20,7 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
@@ -31,8 +32,8 @@
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
- // Package private for use in VcnGatewayConnectionConfigTest
- static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
+ // Public for use in UnderlyingNetworkControllerTest
+ public static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
.setMetered(MATCH_FORBIDDEN)
.setMinUpstreamBandwidthKbps(
@@ -44,13 +45,44 @@
.setOperatorPlmnIds(ALLOWED_PLMN_IDS)
.setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
.setRoaming(MATCH_FORBIDDEN)
- .setOpportunistic(MATCH_REQUIRED)
- .build();
+ .setOpportunistic(MATCH_REQUIRED);
+ }
+
+ // Package private for use in VcnGatewayConnectionConfigTest
+ static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
+ return getTestNetworkTemplateBuilder().build();
+ }
+
+ private void setAllCapabilities(
+ VcnCellUnderlyingNetworkTemplate.Builder builder, int matchCriteria) {
+ builder.setCbs(matchCriteria);
+ builder.setDun(matchCriteria);
+ builder.setIms(matchCriteria);
+ builder.setInternet(matchCriteria);
+ builder.setMms(matchCriteria);
+ builder.setRcs(matchCriteria);
+ }
+
+ private void verifyAllCapabilities(
+ VcnCellUnderlyingNetworkTemplate template,
+ int expectMatchCriteriaforNonInternet,
+ int expectMatchCriteriaforInternet) {
+ assertEquals(expectMatchCriteriaforNonInternet, template.getCbs());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getDun());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getIms());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getMms());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getRcs());
+
+ assertEquals(expectMatchCriteriaforInternet, template.getInternet());
}
@Test
public void testBuilderAndGetters() {
- final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ final VcnCellUnderlyingNetworkTemplate.Builder builder = getTestNetworkTemplateBuilder();
+ setAllCapabilities(builder, MATCH_REQUIRED);
+
+ final VcnCellUnderlyingNetworkTemplate networkPriority = builder.build();
+
assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
assertEquals(
TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
@@ -68,6 +100,8 @@
assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
+
+ verifyAllCapabilities(networkPriority, MATCH_REQUIRED, MATCH_REQUIRED);
}
@Test
@@ -86,6 +120,8 @@
assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
assertEquals(MATCH_ANY, networkPriority.getRoaming());
assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
+
+ verifyAllCapabilities(networkPriority, MATCH_ANY, MATCH_REQUIRED);
}
@Test
@@ -112,6 +148,25 @@
}
@Test
+ public void testBuildFailWithoutRequiredCapabilities() {
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder().setInternet(MATCH_ANY).build();
+
+ fail("Expected IAE for missing required capabilities");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testEqualsWithDifferentCapabilities() {
+ final VcnCellUnderlyingNetworkTemplate left =
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build();
+ final VcnCellUnderlyingNetworkTemplate right =
+ new VcnCellUnderlyingNetworkTemplate.Builder().setMms(MATCH_REQUIRED).build();
+ assertNotEquals(left, right);
+ }
+
+ @Test
public void testPersistableBundle() {
final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
@@ -119,4 +174,16 @@
VcnUnderlyingNetworkTemplate.fromPersistableBundle(
networkPriority.toPersistableBundle()));
}
+
+ @Test
+ public void testPersistableBundleWithCapabilities() {
+ final VcnCellUnderlyingNetworkTemplate.Builder builder = getTestNetworkTemplateBuilder();
+ setAllCapabilities(builder, MATCH_REQUIRED);
+
+ final VcnCellUnderlyingNetworkTemplate networkPriority = builder.build();
+ assertEquals(
+ networkPriority,
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
+ networkPriority.toPersistableBundle()));
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
index 7ac51b7..b313c9f 100644
--- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -16,7 +16,12 @@
package android.net.vcn;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -24,6 +29,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.os.Parcel;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -42,19 +48,36 @@
private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
+ private static final Set<Integer> RESTRICTED_TRANSPORTS = new ArraySet<>();
+
+ static {
+ RESTRICTED_TRANSPORTS.add(TRANSPORT_WIFI);
+ RESTRICTED_TRANSPORTS.add(TRANSPORT_CELLULAR);
+ }
+
private final Context mContext = mock(Context.class);
// Public visibility for VcnManagementServiceTest
- public static VcnConfig buildTestConfig(@NonNull Context context) {
+ public static VcnConfig buildTestConfig(
+ @NonNull Context context, Set<Integer> restrictedTransports) {
VcnConfig.Builder builder = new VcnConfig.Builder(context);
for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
builder.addGatewayConnectionConfig(gatewayConnectionConfig);
}
+ if (restrictedTransports != null) {
+ builder.setRestrictedUnderlyingNetworkTransports(restrictedTransports);
+ }
+
return builder.build();
}
+ // Public visibility for VcnManagementServiceTest
+ public static VcnConfig buildTestConfig(@NonNull Context context) {
+ return buildTestConfig(context, null);
+ }
+
@Before
public void setUp() throws Exception {
doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName();
@@ -91,11 +114,25 @@
}
@Test
- public void testBuilderAndGetters() {
+ public void testBuilderAndGettersDefaultValues() {
final VcnConfig config = buildTestConfig(mContext);
assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName());
assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ assertFalse(config.isTestModeProfile());
+ assertEquals(
+ Collections.singleton(TRANSPORT_WIFI),
+ config.getRestrictedUnderlyingNetworkTransports());
+ }
+
+ @Test
+ public void testBuilderAndGettersConfigRestrictedTransports() {
+ final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+
+ assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName());
+ assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ assertFalse(config.isTestModeProfile());
+ assertEquals(RESTRICTED_TRANSPORTS, config.getRestrictedUnderlyingNetworkTransports());
}
@Test
@@ -106,6 +143,24 @@
}
@Test
+ public void testPersistableBundleWithRestrictedTransports() {
+ final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+
+ assertEquals(config, new VcnConfig(config.toPersistableBundle()));
+ }
+
+ @Test
+ public void testEqualityWithRestrictedTransports() {
+ final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+ final VcnConfig configEqual = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+ final VcnConfig configNotEqual =
+ buildTestConfig(mContext, Collections.singleton(TRANSPORT_WIFI));
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
+
+ @Test
public void testParceling() {
final VcnConfig config = buildTestConfig(mContext);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 2aef9ae..1883c85 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -19,9 +19,11 @@
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -42,7 +44,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -79,6 +83,9 @@
};
public static final int MAX_MTU = 1360;
+ private static final Set<Integer> GATEWAY_OPTIONS =
+ Collections.singleton(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY);
+
public static final IkeTunnelConnectionParams TUNNEL_CONNECTION_PARAMS =
TunnelConnectionParamsUtilsTest.buildTestParams();
@@ -93,14 +100,20 @@
EXPOSED_CAPS);
}
- // Public for use in VcnGatewayConnectionTest
- public static VcnGatewayConnectionConfig buildTestConfig() {
+ // Public for use in UnderlyingNetworkControllerTest
+ public static VcnGatewayConnectionConfig buildTestConfig(
+ List<VcnUnderlyingNetworkTemplate> nwTemplates) {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
+ newBuilder().setVcnUnderlyingNetworkPriorities(nwTemplates);
return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
+ }
+
private static VcnGatewayConnectionConfig.Builder newBuilder() {
// Append a unique identifier to the name prefix to guarantee that all created
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
@@ -109,10 +122,16 @@
TUNNEL_CONNECTION_PARAMS);
}
- private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
- VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions(
+ VcnGatewayConnectionConfig.Builder builder,
+ Set<Integer> gatewayOptions,
+ int... exposedCaps) {
builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ for (int option : gatewayOptions) {
+ builder.addGatewayOption(option);
+ }
+
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
@@ -120,11 +139,28 @@
return builder.build();
}
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
+ VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ return buildTestConfigWithExposedCapsAndOptions(
+ builder, Collections.emptySet(), exposedCaps);
+ }
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps);
}
+ private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ VcnGatewayConnectionConfig.Builder builder, Set<Integer> gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(builder, gatewayOptions, EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ Set<Integer> gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(newBuilder(), gatewayOptions, EXPOSED_CAPS);
+ }
+
@Test
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
@@ -211,6 +247,15 @@
}
@Test
+ public void testBuilderRequiresValidOption() {
+ try {
+ newBuilder().addGatewayOption(-1);
+ fail("Expected exception due to the invalid VCN gateway option");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
@@ -225,6 +270,20 @@
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
+
+ assertFalse(
+ config.hasGatewayOption(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY));
+ }
+
+ @Test
+ public void testBuilderAndGettersWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ for (int option : GATEWAY_OPTIONS) {
+ assertTrue(config.hasGatewayOption(option));
+ }
}
@Test
@@ -235,6 +294,14 @@
}
@Test
+ public void testPersistableBundleWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+
+ @Test
public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
@@ -318,4 +385,27 @@
assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
assertNotEquals(config, configNotEqual);
}
+
+ private static VcnGatewayConnectionConfig buildConfigWithGatewayOptionsForEqualityTest(
+ Set<Integer> gatewayOptions) {
+ return buildTestConfigWithGatewayOptions(
+ new VcnGatewayConnectionConfig.Builder(
+ "buildConfigWithGatewayOptionsForEqualityTest", TUNNEL_CONNECTION_PARAMS),
+ gatewayOptions);
+ }
+
+ @Test
+ public void testVcnGatewayOptionsEquality() throws Exception {
+ final VcnGatewayConnectionConfig config =
+ buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS);
+
+ final VcnGatewayConnectionConfig configEqual =
+ buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS);
+
+ final VcnGatewayConnectionConfig configNotEqual =
+ buildConfigWithGatewayOptionsForEqualityTest(Collections.emptySet());
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 258642ac..075bc5e 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -258,7 +258,7 @@
doReturn(Collections.singleton(TRANSPORT_WIFI))
.when(mMockDeps)
- .getRestrictedTransports(any(), any());
+ .getRestrictedTransports(any(), any(), any());
}
@@ -1038,18 +1038,18 @@
new LinkProperties());
}
- private void checkGetRestrictedTransports(
+ private void checkGetRestrictedTransportsFromCarrierConfig(
ParcelUuid subGrp,
TelephonySubscriptionSnapshot lastSnapshot,
Set<Integer> expectedTransports) {
Set<Integer> result =
new VcnManagementService.Dependencies()
- .getRestrictedTransports(subGrp, lastSnapshot);
+ .getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot);
assertEquals(expectedTransports, result);
}
@Test
- public void testGetRestrictedTransports() {
+ public void testGetRestrictedTransportsFromCarrierConfig() {
final Set<Integer> restrictedTransports = new ArraySet<>();
restrictedTransports.add(TRANSPORT_CELLULAR);
restrictedTransports.add(TRANSPORT_WIFI);
@@ -1065,11 +1065,12 @@
mock(TelephonySubscriptionSnapshot.class);
doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
- checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports);
+ checkGetRestrictedTransportsFromCarrierConfig(
+ TEST_UUID_2, lastSnapshot, restrictedTransports);
}
@Test
- public void testGetRestrictedTransports_noRestrictPolicyConfigured() {
+ public void testGetRestrictedTransportsFromCarrierConfig_noRestrictPolicyConfigured() {
final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI);
final PersistableBundleWrapper carrierConfig =
@@ -1078,17 +1079,54 @@
mock(TelephonySubscriptionSnapshot.class);
doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
- checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports);
+ checkGetRestrictedTransportsFromCarrierConfig(
+ TEST_UUID_2, lastSnapshot, restrictedTransports);
}
@Test
- public void testGetRestrictedTransports_noCarrierConfig() {
+ public void testGetRestrictedTransportsFromCarrierConfig_noCarrierConfig() {
final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI);
final TelephonySubscriptionSnapshot lastSnapshot =
mock(TelephonySubscriptionSnapshot.class);
- checkGetRestrictedTransports(TEST_UUID_2, lastSnapshot, restrictedTransports);
+ checkGetRestrictedTransportsFromCarrierConfig(
+ TEST_UUID_2, lastSnapshot, restrictedTransports);
+ }
+
+ @Test
+ public void testGetRestrictedTransportsFromCarrierConfigAndVcnConfig() {
+ // Configure restricted transport in CarrierConfig
+ final Set<Integer> restrictedTransportInCarrierConfig =
+ Collections.singleton(TRANSPORT_WIFI);
+
+ PersistableBundle carrierConfigBundle = new PersistableBundle();
+ carrierConfigBundle.putIntArray(
+ VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ restrictedTransportInCarrierConfig.stream().mapToInt(i -> i).toArray());
+ final PersistableBundleWrapper carrierConfig =
+ new PersistableBundleWrapper(carrierConfigBundle);
+
+ final TelephonySubscriptionSnapshot lastSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
+
+ // Configure restricted transport in VcnConfig
+ final Context mockContext = mock(Context.class);
+ doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName();
+ final VcnConfig vcnConfig =
+ VcnConfigTest.buildTestConfig(
+ mockContext, Collections.singleton(TRANSPORT_CELLULAR));
+
+ // Verifications
+ final Set<Integer> expectedTransports = new ArraySet<>();
+ expectedTransports.add(TRANSPORT_CELLULAR);
+ expectedTransports.add(TRANSPORT_WIFI);
+
+ Set<Integer> result =
+ new VcnManagementService.Dependencies()
+ .getRestrictedTransports(TEST_UUID_2, lastSnapshot, vcnConfig);
+ assertEquals(expectedTransports, result);
}
private void checkGetUnderlyingNetworkPolicy(
@@ -1103,7 +1141,7 @@
if (isTransportRestricted) {
restrictedTransports.add(transportType);
}
- doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any());
+ doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any());
final VcnUnderlyingNetworkPolicy policy =
startVcnAndGetPolicyForTransport(
@@ -1201,7 +1239,7 @@
public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception {
doReturn(Collections.singleton(TRANSPORT_WIFI))
.when(mMockDeps)
- .getRestrictedTransports(any(), any());
+ .getRestrictedTransports(any(), any(), any());
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */);
@@ -1344,6 +1382,23 @@
}
@Test
+ public void testVcnConfigChangeUpdatesPolicyListener() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ final Context mockContext = mock(Context.class);
+ doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName();
+ final VcnConfig vcnConfig =
+ VcnConfigTest.buildTestConfig(
+ mockContext, Collections.singleton(TRANSPORT_CELLULAR));
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
public void testRemoveVcnUpdatesPolicyListener() throws Exception {
setupActiveSubscription(TEST_UUID_2);
@@ -1375,7 +1430,7 @@
setupActiveSubscription(TEST_UUID_2);
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
- mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListenerForTest(mMockPolicyListener);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
final TelephonySubscriptionSnapshot snapshot =
buildSubscriptionSnapshot(
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 15d4f10..aad7a5e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -50,23 +50,29 @@
import static java.util.Collections.singletonList;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager.VcnErrorCode;
+import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.MtuUtils;
import org.junit.Before;
@@ -86,8 +92,11 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+ private static final int PARALLEL_SA_COUNT = 4;
+
private VcnIkeSession mIkeSession;
private VcnNetworkAgent mNetworkAgent;
+ private Network mVcnNetwork;
@Before
public void setUp() throws Exception {
@@ -98,6 +107,9 @@
.when(mDeps)
.newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any());
+ mVcnNetwork = mock(Network.class);
+ doReturn(mVcnNetwork).when(mNetworkAgent).getNetwork();
+
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
@@ -166,19 +178,82 @@
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
}
+ private void verifyDataStallTriggersMigration(
+ UnderlyingNetworkRecord networkRecord,
+ Network networkWithDataStall,
+ boolean expectMobilityUpdate)
+ throws Exception {
+ mGatewayConnection.setUnderlyingNetwork(networkRecord);
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ final DataStallReport report =
+ new DataStallReport(
+ networkWithDataStall,
+ 1234 /* reportTimestamp */,
+ 1 /* detectionMethod */,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ new PersistableBundle());
+
+ mGatewayConnection.getConnectivityDiagnosticsCallback().onDataStallSuspected(report);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+ if (expectMobilityUpdate) {
+ verify(mIkeSession).setNetwork(networkRecord.network);
+ } else {
+ verify(mIkeSession, never()).setNetwork(any(Network.class));
+ }
+ }
+
+ @Test
+ public void testDataStallTriggersMigration() throws Exception {
+ verifyDataStallTriggersMigration(
+ TEST_UNDERLYING_NETWORK_RECORD_1, mVcnNetwork, true /* expectMobilityUpdate */);
+ }
+
+ @Test
+ public void testDataStallWontTriggerMigrationWhenOnOtherNetwork() throws Exception {
+ verifyDataStallTriggersMigration(
+ TEST_UNDERLYING_NETWORK_RECORD_1,
+ mock(Network.class),
+ false /* expectMobilityUpdate */);
+ }
+
+ @Test
+ public void testDataStallWontTriggerMigrationWhenUnderlyingNetworkLost() throws Exception {
+ verifyDataStallTriggersMigration(
+ null /* networkRecord */, mock(Network.class), false /* expectMobilityUpdate */);
+ }
+
private void verifyVcnTransformsApplied(
VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform)
throws Exception {
+ verifyVcnTransformsApplied(
+ vcnGatewayConnection,
+ expectForwardTransform,
+ Collections.singletonList(getChildSessionCallback()));
+ }
+
+ private void verifyVcnTransformsApplied(
+ VcnGatewayConnection vcnGatewayConnection,
+ boolean expectForwardTransform,
+ List<VcnChildSessionCallback> callbacks)
+ throws Exception {
for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
- getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ }
mTestLooper.dispatchAll();
- verify(mIpSecSvc)
+ verify(mIpSecSvc, times(callbacks.size()))
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
}
- verify(mIpSecSvc, expectForwardTransform ? times(1) : never())
+ verify(mIpSecSvc, expectForwardTransform ? times(callbacks.size()) : never())
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any());
@@ -358,6 +433,89 @@
verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
}
+ private List<VcnChildSessionCallback> openChildAndVerifyParallelSasRequested()
+ throws Exception {
+ doReturn(PARALLEL_SA_COUNT)
+ .when(mDeps)
+ .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP));
+
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ // Verify new child sessions requested
+ final ArgumentCaptor<VcnChildSessionCallback> captor =
+ ArgumentCaptor.forClass(VcnChildSessionCallback.class);
+ verify(mIkeSession, times(PARALLEL_SA_COUNT - 1))
+ .openChildSession(any(TunnelModeChildSessionParams.class), captor.capture());
+
+ return captor.getAllValues();
+ }
+
+ private List<VcnChildSessionCallback> verifyChildOpenedRequestsAndAppliesParallelSas()
+ throws Exception {
+ List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested();
+
+ verifyVcnTransformsApplied(mGatewayConnection, false, callbacks);
+
+ // Mock IKE calling of onOpened()
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onOpened(mock(VcnChildSessionConfiguration.class));
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ return callbacks;
+ }
+
+ @Test
+ public void testChildOpenedWithParallelSas() throws Exception {
+ verifyChildOpenedRequestsAndAppliesParallelSas();
+ }
+
+ @Test
+ public void testOpportunisticSa_ignoresPreOpenFailures() throws Exception {
+ List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested();
+
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onClosed();
+ cb.onClosedExceptionally(mock(IkeException.class));
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+ }
+
+ private void verifyPostOpenFailuresCloseSession(boolean shouldCloseWithException)
+ throws Exception {
+ List<VcnChildSessionCallback> callbacks = verifyChildOpenedRequestsAndAppliesParallelSas();
+
+ for (VcnChildSessionCallback cb : callbacks) {
+ if (shouldCloseWithException) {
+ cb.onClosed();
+ } else {
+ cb.onClosedExceptionally(mock(IkeException.class));
+ }
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ @Test
+ public void testOpportunisticSa_handlesPostOpenFailures_onClosed() throws Exception {
+ verifyPostOpenFailuresCloseSession(false /* shouldCloseWithException */);
+ }
+
+ @Test
+ public void testOpportunisticSa_handlesPostOpenFailures_onClosedExceptionally()
+ throws Exception {
+ verifyPostOpenFailuresCloseSession(true /* shouldCloseWithException */);
+ }
+
@Test
public void testInternalAndDnsAddressesChanged() throws Exception {
final List<LinkAddress> startingInternalAddrs =
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 6a9a1e2..692c8a8 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -24,6 +24,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
@@ -34,20 +35,25 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import android.net.IpSecManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
@@ -64,6 +70,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
import java.util.Arrays;
@@ -71,7 +78,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.Executor;
/** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class)
@@ -134,9 +143,9 @@
capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH);
capBuilder.setAdministratorUids(new int[] {TEST_UID});
final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS);
- UnderlyingNetworkRecord record = new UnderlyingNetworkRecord(
- underlyingNetwork,
- capBuilder.build(), new LinkProperties(), false);
+ UnderlyingNetworkRecord record =
+ getTestNetworkRecord(
+ underlyingNetwork, capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities(
VcnGatewayConnectionConfigTest.buildTestConfig(),
@@ -202,7 +211,7 @@
doReturn(TEST_DNS_ADDRESSES).when(childSessionConfig).getInternalDnsServers();
UnderlyingNetworkRecord record =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities.Builder().build(),
underlyingLp,
@@ -287,5 +296,60 @@
verify(vcnNetworkAgent).unregister();
verifyWakeLockReleased();
+
+ verify(mConnDiagMgr)
+ .unregisterConnectivityDiagnosticsCallback(
+ mGatewayConnection.getConnectivityDiagnosticsCallback());
+ }
+
+ private VcnGatewayConnection buildConnectionWithDataStallHandling(
+ boolean datatStallHandlingEnabled) throws Exception {
+ Set<Integer> options =
+ datatStallHandlingEnabled
+ ? Collections.singleton(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)
+ : Collections.emptySet();
+ final VcnGatewayConnectionConfig gatewayConfig =
+ VcnGatewayConnectionConfigTest.buildTestConfigWithGatewayOptions(options);
+ final VcnGatewayConnection gatewayConnection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ gatewayConfig,
+ mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
+ mDeps);
+ return gatewayConnection;
+ }
+
+ @Test
+ public void testDataStallHandlingEnabled() throws Exception {
+ final VcnGatewayConnection gatewayConnection =
+ buildConnectionWithDataStallHandling(true /* datatStallHandlingEnabled */);
+
+ final ArgumentCaptor<NetworkRequest> networkRequestCaptor =
+ ArgumentCaptor.forClass(NetworkRequest.class);
+ verify(mConnDiagMgr)
+ .registerConnectivityDiagnosticsCallback(
+ networkRequestCaptor.capture(),
+ any(Executor.class),
+ eq(gatewayConnection.getConnectivityDiagnosticsCallback()));
+
+ final NetworkRequest nr = networkRequestCaptor.getValue();
+ final NetworkRequest expected =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+ assertEquals(expected, nr);
+ }
+
+ @Test
+ public void testDataStallHandlingDisabled() throws Exception {
+ buildConnectionWithDataStallHandling(false /* datatStallHandlingEnabled */);
+
+ verify(mConnDiagMgr, never())
+ .registerConnectivityDiagnosticsCallback(
+ any(NetworkRequest.class),
+ any(Executor.class),
+ any(ConnectivityDiagnosticsCallback.class));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 785bff1..bb123ff 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -35,6 +35,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpSecConfig;
@@ -108,9 +109,23 @@
protected static final long ELAPSED_REAL_TIME = 123456789L;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+ protected static UnderlyingNetworkRecord getTestNetworkRecord(
+ Network network,
+ NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties,
+ boolean isBlocked) {
+ return new UnderlyingNetworkRecord(
+ network,
+ networkCapabilities,
+ linkProperties,
+ isBlocked,
+ false /* isSelected */,
+ 0 /* priorityClass */);
+ }
+
protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(),
new LinkProperties(),
@@ -123,7 +138,7 @@
protected static final String TEST_TCP_BUFFER_SIZES_2 = "2,3,4,5";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(),
new LinkProperties(),
@@ -157,6 +172,7 @@
@NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr;
+ @NonNull protected final ConnectivityDiagnosticsManager mConnDiagMgr;
@NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
@NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
@@ -186,6 +202,13 @@
VcnTestUtils.setupSystemService(
mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ mConnDiagMgr = mock(ConnectivityDiagnosticsManager.class);
+ VcnTestUtils.setupSystemService(
+ mContext,
+ mConnDiagMgr,
+ Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class);
+
mIkeConnectionInfo =
new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();
@@ -200,6 +223,9 @@
doReturn(mWakeLock)
.when(mDeps)
.newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
+ doReturn(1)
+ .when(mDeps)
+ .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP));
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index b0d6895..629e988 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,6 +16,7 @@
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
@@ -24,8 +25,8 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.calculatePriorityClass;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
@@ -48,6 +49,7 @@
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -64,6 +66,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -102,6 +106,7 @@
private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setSubscriptionIds(Set.of(SUB_ID))
.setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
@@ -135,25 +140,35 @@
false /* isInTestMode */));
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
- mWifiNetworkRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- WIFI_NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- false /* isBlocked */);
-
- mCellNetworkRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- CELL_NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- false /* isBlocked */);
-
setupSystemService(
mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+
+ mWifiNetworkRecord =
+ getTestNetworkRecord(
+ WIFI_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ mCellNetworkRecord =
+ getTestNetworkRecord(
+ CELL_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ }
+
+ private UnderlyingNetworkRecord getTestNetworkRecord(
+ NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+ return new UnderlyingNetworkRecord(
+ mNetwork,
+ nc,
+ LINK_PROPERTIES,
+ false /* isBlocked */,
+ mVcnContext,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null /* currentlySelected */,
+ null /* carrierConfig */);
}
@Test
@@ -490,37 +505,72 @@
mSubscriptionSnapshot));
}
- private void verifyCalculatePriorityClass(
- UnderlyingNetworkRecord networkRecord, int expectedIndex) {
- final int priorityIndex =
- calculatePriorityClass(
+ private void verifyMatchCellWithRequiredCapabilities(
+ VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) {
+ assertEquals(
+ expectMatch,
+ checkMatchesCellPriorityRule(
mVcnContext,
- networkRecord,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ template,
+ mCellNetworkRecord,
SUB_GROUP,
- mSubscriptionSnapshot,
- null /* currentlySelected */,
- null /* carrierConfig */);
+ mSubscriptionSnapshot));
+ }
- assertEquals(expectedIndex, priorityIndex);
+ @Test
+ public void testMatchCell() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setInternet(MATCH_REQUIRED).build();
+ verifyMatchCellWithRequiredCapabilities(template, true /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchCellFail_RequiredCapabilitiesMissing() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setCbs(MATCH_REQUIRED).build();
+ verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchCellFail_ForbiddenCapabilitiesFound() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setDun(MATCH_FORBIDDEN).build();
+ verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
}
@Test
public void testCalculatePriorityClass() throws Exception {
- verifyCalculatePriorityClass(mCellNetworkRecord, 2);
+ assertEquals(2, mCellNetworkRecord.priorityClass);
+ }
+
+ private void checkCalculatePriorityClassFailToMatchAny(
+ boolean hasInternet, int expectedPriorityClass) throws Exception {
+ final List<VcnUnderlyingNetworkTemplate> templatesRequireDun =
+ Collections.singletonList(
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setDun(MATCH_REQUIRED)
+ .build());
+
+ final NetworkCapabilities.Builder ncBuilder =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ if (hasInternet) {
+ ncBuilder.addCapability(NET_CAPABILITY_INTERNET);
+ }
+
+ final UnderlyingNetworkRecord nonDunNetworkRecord =
+ getTestNetworkRecord(ncBuilder.build(), templatesRequireDun);
+
+ assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass);
}
@Test
- public void testCalculatePriorityClassFailToMatchAny() throws Exception {
- final NetworkCapabilities nc =
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSignalStrength(WIFI_RSSI_LOW)
- .setSsid(SSID)
- .build();
- final UnderlyingNetworkRecord wifiNetworkRecord =
- new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */);
+ public void testCalculatePriorityClassFailToMatchAny_InternetNetwork() throws Exception {
+ checkCalculatePriorityClassFailToMatchAny(true /* hasInternet */, PRIORITY_FALLBACK);
+ }
- verifyCalculatePriorityClass(wifiNetworkRecord, PRIORITY_ANY);
+ @Test
+ public void testCalculatePriorityClassFailToMatchAny_NonInternetNetwork() throws Exception {
+ checkCalculatePriorityClassFailToMatchAny(false /* hasInternet */, PRIORITY_INVALID);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index fad9669..2941fde 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -16,18 +16,29 @@
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_REQUIRED;
+
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
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.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -42,7 +53,10 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
@@ -64,7 +78,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -95,11 +112,39 @@
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
+ private static final NetworkCapabilities DUN_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+
+ private static final NetworkCapabilities CBS_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+
private static final LinkProperties INITIAL_LINK_PROPERTIES =
getLinkPropertiesWithName("initial_iface");
private static final LinkProperties UPDATED_LINK_PROPERTIES =
getLinkPropertiesWithName("updated_iface");
+ private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_DUN =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setInternet(MATCH_ANY)
+ .setDun(MATCH_REQUIRED)
+ .build();
+
+ private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_CBS =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setInternet(MATCH_ANY)
+ .setCbs(MATCH_REQUIRED)
+ .build();
+
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
@@ -201,6 +246,107 @@
any());
}
+ private void verifyRequestBackgroundNetwork(
+ ConnectivityManager cm,
+ int expectedSubId,
+ Set<Integer> expectedRequiredCaps,
+ Set<Integer> expectedForbiddenCaps) {
+ verify(cm)
+ .requestBackgroundNetwork(
+ eq(
+ getCellRequestForSubId(
+ expectedSubId,
+ expectedRequiredCaps,
+ expectedForbiddenCaps)),
+ any(NetworkBringupCallback.class),
+ any());
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupForNonInternetCapabilities() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ // Build network templates
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
+ .setDun(MATCH_REQUIRED)
+ .setInternet(MATCH_ANY)
+ .build());
+
+ networkTemplates.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
+ .setMms(MATCH_REQUIRED)
+ .setCbs(MATCH_FORBIDDEN)
+ .setInternet(MATCH_ANY)
+ .build());
+
+ // Start UnderlyingNetworkController
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ // Verifications
+ for (final int subId : INITIAL_SUB_IDS) {
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm, subId, Collections.singleton(NET_CAPABILITY_DUN), Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_MMS),
+ Collections.singleton(NET_CAPABILITY_CBS));
+ }
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupWithDedupedtCapabilities() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ // Build network templates
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ final VcnCellUnderlyingNetworkTemplate.Builder builder =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMms(MATCH_REQUIRED)
+ .setCbs(MATCH_FORBIDDEN)
+ .setInternet(MATCH_ANY);
+
+ networkTemplates.add(builder.setMetered(MATCH_REQUIRED).build());
+ networkTemplates.add(builder.setMetered(MATCH_FORBIDDEN).build());
+
+ // Start UnderlyingNetworkController
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ // Verifications
+ for (final int subId : INITIAL_SUB_IDS) {
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_MMS),
+ Collections.singleton(NET_CAPABILITY_CBS));
+ }
+ }
+
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
@@ -210,8 +356,13 @@
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
- eq(getCellRequestForSubId(subId)),
- any(NetworkBringupCallback.class), any());
+ eq(
+ getCellRequestForSubId(
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet())),
+ any(NetworkBringupCallback.class),
+ any());
}
verify(mConnectivityManager)
@@ -253,6 +404,7 @@
private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.build();
}
@@ -261,6 +413,7 @@
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
.build();
@@ -270,16 +423,27 @@
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
.build();
}
- private NetworkRequest getCellRequestForSubId(int subId) {
- return getExpectedRequestBase()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
- .build();
+ private NetworkRequest getCellRequestForSubId(
+ int subId, Set<Integer> requiredCaps, Set<Integer> forbiddenCaps) {
+ final NetworkRequest.Builder nqBuilder =
+ getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+
+ for (int cap : requiredCaps) {
+ nqBuilder.addCapability(cap);
+ }
+ for (int cap : forbiddenCaps) {
+ nqBuilder.addForbiddenCapability(cap);
+ }
+
+ return nqBuilder.build();
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
@@ -301,7 +465,6 @@
private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
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);
@@ -321,16 +484,30 @@
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
}
+ private static UnderlyingNetworkRecord getTestNetworkRecord(
+ Network network,
+ NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties,
+ boolean isBlocked) {
+ return new UnderlyingNetworkRecord(
+ network,
+ networkCapabilities,
+ linkProperties,
+ isBlocked,
+ false /* isSelected */,
+ 0 /* priorityClass */);
+ }
+
@Test
public void testUnderlyingNetworkRecordEquals() {
UnderlyingNetworkRecord recordA =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
UnderlyingNetworkRecord recordB =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
@@ -338,12 +515,24 @@
UnderlyingNetworkRecord recordC =
new UnderlyingNetworkRecord(
mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */,
+ true /* isSelected */,
+ -1 /* priorityClass */);
+ UnderlyingNetworkRecord recordD =
+ getTestNetworkRecord(
+ mNetwork,
UPDATED_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
assertEquals(recordA, recordB);
- assertNotEquals(recordA, recordC);
+ assertEquals(recordA, recordC);
+ assertNotEquals(recordA, recordD);
+
+ assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB));
+ assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC));
}
@Test
@@ -366,6 +555,10 @@
.build();
}
+ private void verifyOnSelectedUnderlyingNetworkChanged(UnderlyingNetworkRecord expectedRecord) {
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
NetworkCapabilities networkCapabilities) {
verify(mConnectivityManager)
@@ -384,12 +577,12 @@
cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
return cb;
}
@@ -402,12 +595,12 @@
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -417,12 +610,12 @@
cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -434,18 +627,16 @@
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -458,18 +649,16 @@
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -478,13 +667,7 @@
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
- UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
- INITIAL_LINK_PROPERTIES,
- true /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(null);
}
@Test
@@ -520,5 +703,132 @@
verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
}
- // TODO (b/187991063): Add tests for network prioritization
+ private UnderlyingNetworkListener setupControllerAndGetNetworkListener(
+ List<VcnUnderlyingNetworkTemplate> networkTemplates) {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ verify(cm)
+ .registerNetworkCallback(
+ eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
+ mUnderlyingNetworkListenerCaptor.capture(),
+ any());
+
+ return mUnderlyingNetworkListenerCaptor.getValue();
+ }
+
+ private UnderlyingNetworkRecord bringupNetworkAndGetRecord(
+ UnderlyingNetworkListener cb,
+ NetworkCapabilities requestNetworkCaps,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ UnderlyingNetworkRecord currentlySelected) {
+ final Network network = mock(Network.class);
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS);
+
+ cb.onAvailable(network);
+ cb.onCapabilitiesChanged(network, responseNetworkCaps);
+ cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES);
+ cb.onBlockedStatusChanged(network, false /* isFalse */);
+ return new UnderlyingNetworkRecord(
+ network,
+ responseNetworkCaps,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */,
+ mVcnContext,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */);
+ }
+
+ @Test
+ public void testSelectMorePreferredNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ networkTemplates.add(CELL_TEMPLATE_DUN);
+ networkTemplates.add(CELL_TEMPLATE_CBS);
+
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up CBS network
+ final UnderlyingNetworkRecord cbsNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ CBS_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+
+ // Bring up DUN network
+ final UnderlyingNetworkRecord dunNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+ }
+
+ @Test
+ public void testNeverSelectLessPreferredNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ networkTemplates.add(CELL_TEMPLATE_DUN);
+ networkTemplates.add(CELL_TEMPLATE_CBS);
+
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up DUN network
+ final UnderlyingNetworkRecord dunNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ DUN_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+
+ // Bring up CBS network
+ final UnderlyingNetworkRecord cbsNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord);
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+ }
+
+ @Test
+ public void testFailtoMatchTemplateAndFallBackToInternetNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up an Internet network without DUN capability
+ final UnderlyingNetworkRecord networkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ INITIAL_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord));
+ }
+
+ @Test
+ public void testFailtoMatchTemplateAndNeverFallBackToNonInternetNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ bringupNetworkAndGetRecord(
+ cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */);
+
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class));
+ }
}