Merge "[MS44.1] Add API to query tagged UID summary"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 31fecd1..14da6fb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -177,6 +177,7 @@
last_released: {
api_file: ":android-non-updatable.api.module-lib.latest",
removed_api_file: ":android-non-updatable-removed.api.module-lib.latest",
+ baseline_file: ":android-non-updatable-incompatibilities.api.module-lib.latest",
},
api_lint: {
enabled: true,
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index bed3895..2c5965c 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 1344
hdmoon@google.com
-hkuang@google.com
jinpark@google.com
klhyun@google.com
lnilsson@google.com
diff --git a/api/Android.bp b/api/Android.bp
index c6ea175..f89c557 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,6 +24,19 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+bootstrap_go_package {
+ name: "soong-api",
+ pkgPath: "android/soong/api",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-genrule",
+ ],
+ srcs: ["api.go"],
+ pluginFor: ["soong_build"],
+}
+
python_defaults {
name: "python3_version_defaults",
version: {
diff --git a/api/api.go b/api/api.go
new file mode 100644
index 0000000..74cbece
--- /dev/null
+++ b/api/api.go
@@ -0,0 +1,225 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package api
+
+import (
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/genrule"
+)
+
+// The intention behind this soong plugin is to generate a number of "merged"
+// API-related modules that would otherwise require a large amount of very
+// similar Android.bp boilerplate to define. For example, the merged current.txt
+// API definitions (created by merging the non-updatable current.txt with all
+// the module current.txts). This simplifies the addition of new android
+// modules, by reducing the number of genrules etc a new module must be added to.
+
+// The properties of the combined_apis module type.
+type CombinedApisProperties struct {
+ // Module libraries in the bootclasspath
+ Bootclasspath []string
+ // Module libraries in system server
+ System_server_classpath []string
+}
+
+type CombinedApis struct {
+ android.ModuleBase
+
+ properties CombinedApisProperties
+}
+
+func init() {
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
+}
+
+var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+type genruleProps struct {
+ Name *string
+ Cmd *string
+ Dists []android.Dist
+ Out []string
+ Srcs []string
+ Tools []string
+ Visibility []string
+}
+
+// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
+type MergedTxtDefinition struct {
+ // "current.txt" or "removed.txt"
+ TxtFilename string
+ // The module for the non-updatable / non-module part of the api.
+ BaseTxt string
+ // The list of modules that are relevant for this merged txt.
+ Modules []string
+ // The output tag for each module to use.e.g. {.public.api.txt} for current.txt
+ ModuleTag string
+ // public, system, module-lib or system-server
+ Scope string
+}
+
+func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
+ metalavaCmd := "$(location metalava)"
+ // Silence reflection warnings. See b/168689341
+ metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
+ metalavaCmd += " --quiet --no-banner --format=v2 "
+
+ filename := txt.TxtFilename
+ if txt.Scope != "public" {
+ filename = txt.Scope + "-" + filename
+ }
+
+ props := genruleProps{}
+ props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
+ props.Tools = []string{"metalava"}
+ props.Out = []string{filename}
+ props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+ props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag)
+ props.Dists = []android.Dist{
+ {
+ Targets: []string{"droidcore"},
+ Dir: proptools.StringPtr("api"),
+ Dest: proptools.StringPtr(filename),
+ },
+ {
+ Targets: []string{"sdk"},
+ Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
+ Dest: proptools.StringPtr(txt.TxtFilename),
+ },
+ }
+ props.Visibility = []string{"//visibility:public"}
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
+ props := genruleProps{}
+ props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
+ props.Tools = []string{"merge_zips"}
+ props.Out = []string{"current.srcjar"}
+ props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
+ props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}")
+ props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
+ props := genruleProps{}
+ props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
+ props.Tools = []string{"api_versions_trimmer"}
+ props.Out = []string{"api-versions-public-filtered.xml"}
+ props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
+ // Note: order matters: first parameter is the full api-versions.xml
+ // after that the stubs files in any order
+ // stubs files are all modules that export API surfaces EXCEPT ART
+ props.Srcs = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}")
+ props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createSrcs(base string, modules []string, tag string) []string {
+ a := make([]string, 0, len(modules)+1)
+ a = append(a, base)
+ for _, module := range modules {
+ a = append(a, ":"+module+tag)
+ }
+ return a
+}
+
+func remove(s []string, v string) []string {
+ s2 := make([]string, 0, len(s))
+ for _, sv := range s {
+ if sv != v {
+ s2 = append(s2, sv)
+ }
+ }
+ return s2
+}
+
+func createMergedTxts(ctx android.LoadHookContext, props CombinedApisProperties) {
+ var textFiles []MergedTxtDefinition
+ // Two module libraries currently do not support @SystemApi so only have the public scope.
+ bcpWithSystemApi := props.Bootclasspath
+ bcpWithSystemApi = remove(bcpWithSystemApi, "conscrypt.module.public.api")
+ bcpWithSystemApi = remove(bcpWithSystemApi, "i18n.module.public.api")
+
+ tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
+ for i, f := range []string{"current.txt", "removed.txt"} {
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-" + f,
+ Modules: props.Bootclasspath,
+ ModuleTag: "{.public" + tagSuffix[i],
+ Scope: "public",
+ })
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-system-" + f,
+ Modules: bcpWithSystemApi,
+ ModuleTag: "{.system" + tagSuffix[i],
+ Scope: "system",
+ })
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-module-lib-" + f,
+ Modules: bcpWithSystemApi,
+ ModuleTag: "{.module-lib" + tagSuffix[i],
+ Scope: "module-lib",
+ })
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-system-server-" + f,
+ Modules: props.System_server_classpath,
+ ModuleTag: "{.system-server" + tagSuffix[i],
+ Scope: "system-server",
+ })
+ }
+ for _, txt := range textFiles {
+ createMergedTxt(ctx, txt)
+ }
+}
+
+func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
+ createMergedTxts(ctx, a.properties)
+
+ createMergedStubsSrcjar(ctx, a.properties.Bootclasspath)
+
+ // For the filtered api versions, we prune all APIs except art module's APIs. because
+ // 1) ART apis are available by default to all modules, while other module-to-module deps are
+ // explicit and probably receive more scrutiny anyway
+ // 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
+ // 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
+ // per-module lint databases that excludes just that module's APIs. Alas, that's more
+ // difficult to achieve.
+ filteredModules := a.properties.Bootclasspath
+ filteredModules = remove(filteredModules, "art.module.public.api")
+ createFilteredApiVersions(ctx, filteredModules)
+}
+
+func combinedApisModuleFactory() android.Module {
+ module := &CombinedApis{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
+ return module
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9233941..bb24da1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26896,11 +26896,13 @@
method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress);
method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String);
method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int);
+ method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix);
method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int);
method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String);
method @NonNull public android.net.VpnService.Builder allowBypass();
method @NonNull public android.net.VpnService.Builder allowFamily(int);
method @Nullable public android.os.ParcelFileDescriptor establish();
+ method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix);
method @NonNull public android.net.VpnService.Builder setBlocking(boolean);
method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent);
method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo);
@@ -27277,6 +27279,23 @@
package android.net.vcn {
+ public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
+ method public int getOpportunistic();
+ method public int getRoaming();
+ method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
+ }
+
+ 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 setMetered(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 setRoaming(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
public final class VcnConfig implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
@@ -27295,6 +27314,7 @@
method @NonNull public String getGatewayConnectionName();
method @IntRange(from=0x500) public int getMaxMtu();
method @NonNull public long[] getRetryIntervalsMillis();
+ method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
}
public static final class VcnGatewayConnectionConfig.Builder {
@@ -27304,6 +27324,7 @@
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(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>);
}
public class VcnManager {
@@ -27327,6 +27348,24 @@
method public abstract void onStatusChanged(int);
}
+ public abstract class VcnUnderlyingNetworkTemplate {
+ method public int getMetered();
+ field public static final int MATCH_ANY = 0; // 0x0
+ field public static final int MATCH_FORBIDDEN = 2; // 0x2
+ field public static final int MATCH_REQUIRED = 1; // 0x1
+ }
+
+ public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method @NonNull public java.util.Set<java.lang.String> getSsids();
+ }
+
+ public static final class VcnWifiUnderlyingNetworkTemplate.Builder {
+ ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
+ }
+
}
package android.nfc {
@@ -41215,6 +41254,7 @@
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
+ field public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4; // 0x4
field public static final int ID_TYPE_FQDN = 2; // 0x2
field public static final int ID_TYPE_KEY_ID = 11; // 0xb
field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d13170bf..3300ea0 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -71,6 +71,14 @@
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
+ public class Intent implements java.lang.Cloneable android.os.Parcelable {
+ field public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
+ field public static final String EXTRA_SETTING_NAME = "setting_name";
+ field public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
+ field public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
+ field public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
+ }
+
}
package android.content.pm {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 41ce6f6..29957e4 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -138,6 +138,7 @@
field public static final String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS";
field public static final String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
+ field public static final String MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED = "android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED";
field public static final String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
field public static final String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
@@ -2264,6 +2265,25 @@
package android.bluetooth.le {
+ public final class AdvertiseSettings implements android.os.Parcelable {
+ method public int getOwnAddressType();
+ }
+
+ public static final class AdvertiseSettings.Builder {
+ method @NonNull public android.bluetooth.le.AdvertiseSettings.Builder setOwnAddressType(int);
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int getOwnAddressType();
+ field public static final int ADDRESS_TYPE_DEFAULT = -1; // 0xffffffff
+ field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
+ field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ method @NonNull public android.bluetooth.le.AdvertisingSetParameters.Builder setOwnAddressType(int);
+ }
+
public final class BluetoothLeScanner {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9b37457..c6c64b0 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3086,6 +3086,9 @@
BluetoothCsipSetCoordinator csipSetCoordinator =
new BluetoothCsipSetCoordinator(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
+ BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
+ return true;
} else {
return false;
}
@@ -3188,6 +3191,10 @@
(BluetoothCsipSetCoordinator) proxy;
csipSetCoordinator.close();
break;
+ case BluetoothProfile.LE_CALL_CONTROL:
+ BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
+ tbs.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
new file mode 100644
index 0000000..fb7789d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCall.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Representation of Call
+ *
+ * @hide
+ */
+public final class BluetoothLeCall implements Parcelable {
+
+ /** @hide */
+ @IntDef(prefix = "STATE_", value = {
+ STATE_INCOMING,
+ STATE_DIALING,
+ STATE_ALERTING,
+ STATE_ACTIVE,
+ STATE_LOCALLY_HELD,
+ STATE_REMOTELY_HELD,
+ STATE_LOCALLY_AND_REMOTELY_HELD
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ }
+
+ /**
+ * A remote party is calling (incoming call).
+ *
+ * @hide
+ */
+ public static final int STATE_INCOMING = 0x00;
+
+ /**
+ * The process to call the remote party has started but the remote party is not
+ * being alerted (outgoing call).
+ *
+ * @hide
+ */
+ public static final int STATE_DIALING = 0x01;
+
+ /**
+ * A remote party is being alerted (outgoing call).
+ *
+ * @hide
+ */
+ public static final int STATE_ALERTING = 0x02;
+
+ /**
+ * The call is in an active conversation.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 0x03;
+
+ /**
+ * The call is connected but held locally. “Locally Held” implies that either
+ * the server or the client can affect the state.
+ *
+ * @hide
+ */
+ public static final int STATE_LOCALLY_HELD = 0x04;
+
+ /**
+ * The call is connected but held remotely. “Remotely Held” means that the state
+ * is controlled by the remote party of a call.
+ *
+ * @hide
+ */
+ public static final int STATE_REMOTELY_HELD = 0x05;
+
+ /**
+ * The call is connected but held both locally and remotely.
+ *
+ * @hide
+ */
+ public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
+
+ /**
+ * Whether the call direction is outgoing.
+ *
+ * @hide
+ */
+ public static final int FLAG_OUTGOING_CALL = 0x00000001;
+
+ /**
+ * Whether the call URI and Friendly Name are withheld by server.
+ *
+ * @hide
+ */
+ public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
+
+ /**
+ * Whether the call URI and Friendly Name are withheld by network.
+ *
+ * @hide
+ */
+ public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
+
+ /** Unique UUID that identifies this call */
+ private UUID mUuid;
+
+ /** Remote Caller URI */
+ private String mUri;
+
+ /** Caller friendly name */
+ private String mFriendlyName;
+
+ /** Call state */
+ private @State int mState;
+
+ /** Call flags */
+ private int mCallFlags;
+
+ /** @hide */
+ public BluetoothLeCall(@NonNull BluetoothLeCall that) {
+ mUuid = new UUID(that.getUuid().getMostSignificantBits(),
+ that.getUuid().getLeastSignificantBits());
+ mUri = that.mUri;
+ mFriendlyName = that.mFriendlyName;
+ mState = that.mState;
+ mCallFlags = that.mCallFlags;
+ }
+
+ /** @hide */
+ public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
+ @State int state, int callFlags) {
+ mUuid = uuid;
+ mUri = uri;
+ mFriendlyName = friendlyName;
+ mState = state;
+ mCallFlags = callFlags;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ BluetoothLeCall that = (BluetoothLeCall) o;
+ return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
+ && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
+ && mCallFlags == that.mCallFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
+ }
+
+ /**
+ * Returns a string representation of this BluetoothLeCall.
+ *
+ * <p>
+ * Currently this is the UUID.
+ *
+ * @return string representation of this BluetoothLeCall
+ */
+ @Override
+ public String toString() {
+ return mUuid.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(new ParcelUuid(mUuid), 0);
+ out.writeString(mUri);
+ out.writeString(mFriendlyName);
+ out.writeInt(mState);
+ out.writeInt(mCallFlags);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
+ new Parcelable.Creator<BluetoothLeCall>() {
+ public BluetoothLeCall createFromParcel(Parcel in) {
+ return new BluetoothLeCall(in);
+ }
+
+ public BluetoothLeCall[] newArray(int size) {
+ return new BluetoothLeCall[size];
+ }
+ };
+
+ private BluetoothLeCall(Parcel in) {
+ mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUri = in.readString();
+ mFriendlyName = in.readString();
+ mState = in.readInt();
+ mCallFlags = in.readInt();
+ }
+
+ /**
+ * Returns an UUID of this BluetoothLeCall.
+ *
+ * <p>
+ * An UUID is unique identifier of a BluetoothLeCall.
+ *
+ * @return UUID of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
+ * Returns a URI of the remote party of this BluetoothLeCall.
+ *
+ * @return string representation of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull String getUri() {
+ return mUri;
+ }
+
+ /**
+ * Returns a friendly name of the call.
+ *
+ * @return friendly name representation of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull String getFriendlyName() {
+ return mFriendlyName;
+ }
+
+ /**
+ * Returns the call state.
+ *
+ * @return the state of this BluetoothLeCall
+ * @hide
+ */
+ public @State int getState() {
+ return mState;
+ }
+
+ /**
+ * Returns the call flags.
+ *
+ * @return call flags
+ * @hide
+ */
+ public int getCallFlags() {
+ return mCallFlags;
+ }
+
+ /**
+ * Whether the call direction is incoming.
+ *
+ * @return true if incoming call, false otherwise
+ * @hide
+ */
+ public boolean isIncomingCall() {
+ return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
new file mode 100644
index 0000000..fb080c9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCallControl.java
@@ -0,0 +1,899 @@
+/*
+ * Copyright 2019 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the APIs to control the Call Control profile.
+ *
+ * <p>
+ * This class provides Bluetooth Telephone Bearer Service functionality,
+ * allowing applications to expose a GATT Service based interface to control the
+ * state of the calls by remote devices such as LE audio devices.
+ *
+ * <p>
+ * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothLeCallControl proxy object.
+ *
+ * @hide
+ */
+public final class BluetoothLeCallControl implements BluetoothProfile {
+ private static final String TAG = "BluetoothLeCallControl";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ /** @hide */
+ @IntDef(prefix = "RESULT_", value = {
+ RESULT_SUCCESS,
+ RESULT_ERROR_UNKNOWN_CALL_ID,
+ RESULT_ERROR_INVALID_URI,
+ RESULT_ERROR_APPLICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {
+ }
+
+ /**
+ * Opcode write was successful.
+ *
+ * @hide
+ */
+ public static final int RESULT_SUCCESS = 0;
+
+ /**
+ * Unknown call Id has been used in the operation.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
+
+ /**
+ * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_INVALID_URI = 2;
+
+ /**
+ * Application internal error.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_APPLICATION = 3;
+
+ /** @hide */
+ @IntDef(prefix = "TERMINATION_REASON_", value = {
+ TERMINATION_REASON_INVALID_URI,
+ TERMINATION_REASON_FAIL,
+ TERMINATION_REASON_REMOTE_HANGUP,
+ TERMINATION_REASON_SERVER_HANGUP,
+ TERMINATION_REASON_LINE_BUSY,
+ TERMINATION_REASON_NETWORK_CONGESTION,
+ TERMINATION_REASON_CLIENT_HANGUP,
+ TERMINATION_REASON_NO_SERVICE,
+ TERMINATION_REASON_NO_ANSWER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TerminationReason {
+ }
+
+ /**
+ * Remote Caller ID value used to place a call was formed improperly.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_INVALID_URI = 0x00;
+
+ /**
+ * Call fail.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_FAIL = 0x01;
+
+ /**
+ * Remote party ended call.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
+
+ /**
+ * Call ended from the server.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
+
+ /**
+ * Line busy.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
+
+ /**
+ * Network congestion.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
+
+ /**
+ * Client terminated.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
+
+ /**
+ * No service.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
+
+ /**
+ * No answer.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
+
+ /*
+ * Flag indicating support for hold/unhold call feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_HOLD_CALL = 0x00000001;
+
+ /**
+ * Flag indicating support for joining calls feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
+
+ private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
+ private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
+
+ private static final int REG_TIMEOUT = 10000;
+
+ /**
+ * The template class is used to call callback functions on events from the TBS
+ * server. Callback functions are wrapped in this class and registered to the
+ * Android system during app registration.
+ *
+ * @hide
+ */
+ public abstract static class Callback {
+
+ private static final String TAG = "BluetoothLeCallControl.Callback";
+
+ /**
+ * Called when a remote client requested to accept the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to be accepted
+ * @hide
+ */
+ public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
+
+ /**
+ * A remote client has requested to terminate the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to terminate
+ * @hide
+ */
+ public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
+
+ /**
+ * A remote client has requested to hold the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to be put on hold
+ * @hide
+ */
+ public void onHoldCall(int requestId, @NonNull UUID callId) {
+ Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+ }
+
+ /**
+ * A remote client has requested to unhold the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to unhold
+ * @hide
+ */
+ public void onUnholdCall(int requestId, @NonNull UUID callId) {
+ Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+ }
+
+ /**
+ * A remote client has requested to place a call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The Id to be assigned for the new call
+ * @param uri The caller URI requested
+ * @hide
+ */
+ public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
+
+ /**
+ * A remote client has requested to join the calls.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callIds The call Id list requested to join
+ * @hide
+ */
+ public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
+ Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
+ }
+ }
+
+ private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
+
+ private final Executor mExecutor;
+ private final Callback mCallback;
+
+ CallbackWrapper(Executor executor, Callback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onBearerRegistered(int ccid) {
+ if (mCallback != null) {
+ mCcid = ccid;
+ } else {
+ // registration timeout
+ Log.e(TAG, "onBearerRegistered: mCallback is null");
+ }
+ }
+
+ @Override
+ public void onAcceptCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onTerminateCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onHoldCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onUnholdCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
+ List<UUID> uuids = new ArrayList<>();
+ for (ParcelUuid parcelUuid : parcelUuids) {
+ uuids.add(parcelUuid.getUuid());
+ }
+
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private volatile IBluetoothLeCallControl mService;
+ private BluetoothAdapter mAdapter;
+ private int mCcid = 0;
+ private String mToken;
+ private Callback mCallback = null;
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG)
+ Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ doUnbind();
+ } else {
+ doBind();
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
+ * telephone bearer service.
+ */
+ /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
+ mContext = context;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mServiceListener = listener;
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ doBind();
+ }
+
+ private boolean doBind() {
+ synchronized (mConnection) {
+ if (mService == null) {
+ if (VDBG)
+ Log.d(TAG, "Binding service...");
+ try {
+ return mAdapter.getBluetoothManager().
+ bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+ mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to bind TelephoneBearerService", e);
+ }
+ }
+ }
+ return false;
+ }
+
+ private void doUnbind() {
+ synchronized (mConnection) {
+ if (mService != null) {
+ if (VDBG)
+ Log.d(TAG, "Unbinding service...");
+ try {
+ mAdapter.getBluetoothManager().
+ unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+ mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
+ } finally {
+ mService = null;
+ }
+ }
+ }
+ }
+
+ /* package */ void close() {
+ if (VDBG)
+ log("close()");
+ unregisterBearer();
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ mServiceListener = null;
+ doUnbind();
+ }
+
+ private IBluetoothLeCallControl getService() {
+ return mService;
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public int getConnectionState(@Nullable BluetoothDevice device) {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Register Telephone Bearer exposing the interface that allows remote devices
+ * to track and control the call states.
+ *
+ * <p>
+ * This is an asynchronous call. The callback is used to notify success or
+ * failure if the function returns true.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * <!-- The UCI is a String identifier of the telephone bearer as defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
+ * (login required). -->
+ *
+ * <!-- The examples of common URI schemes can be found in
+ * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
+ *
+ * <!-- The Technology is an integer value. The possible values are defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+ * -->
+ *
+ * @param uci Bearer Unique Client Identifier
+ * @param uriSchemes URI Schemes supported list
+ * @param capabilities bearer capabilities
+ * @param provider Network provider name
+ * @param technology Network technology
+ * @param executor {@link Executor} object on which callback will be
+ * executed. The Executor object is required.
+ * @param callback {@link Callback} object to which callback messages will
+ * be sent. The Callback object is required.
+ * @return true on success, false otherwise
+ * @hide
+ */
+ @SuppressLint("ExecutorRegistration")
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean registerBearer(@Nullable String uci,
+ @NonNull List<String> uriSchemes, int capabilities,
+ @NonNull String provider, int technology,
+ @NonNull Executor executor, @NonNull Callback callback) {
+ if (DBG) {
+ Log.d(TAG, "registerBearer");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("null parameter: " + callback);
+ }
+ if (mCcid != 0) {
+ return false;
+ }
+
+ mToken = uci;
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ if (mCallback != null) {
+ Log.e(TAG, "Bearer can be opened only once");
+ return false;
+ }
+
+ mCallback = callback;
+ try {
+ CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
+ service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
+ provider, technology);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ mCallback = null;
+ return false;
+ }
+
+ if (mCcid == 0) {
+ mCallback = null;
+ return false;
+ }
+
+ return true;
+ }
+
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return false;
+ }
+
+ /**
+ * Unregister Telephone Bearer Service and destroy all the associated data.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void unregisterBearer() {
+ if (DBG) {
+ Log.d(TAG, "unregisterBearer");
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ int ccid = mCcid;
+ mCcid = 0;
+ mCallback = null;
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.unregisterBearer(mToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Get the Content Control ID (CCID) value.
+ *
+ * @return ccid Content Control ID value
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public int getContentControlId() {
+ return mCcid;
+ }
+
+ /**
+ * Notify about the newly added call.
+ *
+ * <p>
+ * This shall be called as early as possible after the call has been added.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param call Newly added call
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallAdded(@NonNull BluetoothLeCall call) {
+ if (DBG) {
+ Log.d(TAG, "onCallAdded: call=" + call);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callAdded(mCcid, call);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Notify about the removed call.
+ *
+ * <p>
+ * This shall be called as early as possible after the call has been removed.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callId The Id of a call that has been removed
+ * @param reason Call termination reason
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
+ if (DBG) {
+ Log.d(TAG, "callRemoved: callId=" + callId);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callRemoved(mCcid, new ParcelUuid(callId), reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Notify the call state change
+ *
+ * <p>
+ * This shall be called as early as possible after the state of the call has
+ * changed.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callId The call Id that state has been changed
+ * @param state Call state
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
+ if (DBG) {
+ Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callStateChanged(mCcid, new ParcelUuid(callId), state);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Provide the current calls list
+ *
+ * <p>
+ * This function must be invoked after registration if application has any
+ * calls.
+ *
+ * @param calls current calls list
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.currentCallsList(mCcid, calls);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ /**
+ * Provide the network current status
+ *
+ * <p>
+ * This function must be invoked on change of network state.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * <!-- The Technology is an integer value. The possible values are defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+ * -->
+ *
+ * @param provider Network provider name
+ * @param technology Network technology
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void networkStateChanged(@NonNull String provider, int technology) {
+ if (DBG) {
+ Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.networkStateChanged(mCcid, provider, technology);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Send a response to a call control request to a remote device.
+ *
+ * <p>
+ * This function must be invoked in when a request is received by one of these
+ * callback methods:
+ *
+ * <ul>
+ * <li>{@link Callback#onAcceptCall}
+ * <li>{@link Callback#onTerminateCall}
+ * <li>{@link Callback#onHoldCall}
+ * <li>{@link Callback#onUnholdCall}
+ * <li>{@link Callback#onPlaceCall}
+ * <li>{@link Callback#onJoinCalls}
+ * </ul>
+ *
+ * @param requestId The ID of the request that was received with the callback
+ * @param result The result of the request to be sent to the remote devices
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void requestResult(int requestId, @Result int result) {
+ if (DBG) {
+ Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.requestResult(mCcid, requestId, result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private final IBluetoothProfileServiceConnection mConnection =
+ new IBluetoothProfileServiceConnection.Stub() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) {
+ Log.d(TAG, "Proxy object connected");
+ }
+ mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) {
+ Log.d(TAG, "Proxy object disconnected");
+ }
+ doUnbind();
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
+ }
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_TBS_SERVICE_CONNECTED: {
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
+ BluetoothLeCallControl.this);
+ }
+ break;
+ }
+ case MESSAGE_TBS_SERVICE_DISCONNECTED: {
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
+ }
+ break;
+ }
+ }
+ }
+ };
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index e047e5d..d0f74e9 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,12 +240,19 @@
int LE_AUDIO_BROADCAST = 26;
/**
+ * @hide
+ * Telephone Bearer Service from Call Control Profile
+ *
+ */
+ int LE_CALL_CONTROL = 27;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 26;
+ int MAX_PROFILE_ID = 27;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
index 7129d76..c52a6ee 100644
--- a/core/java/android/bluetooth/le/AdvertiseSettings.java
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.java
@@ -16,6 +16,9 @@
package android.bluetooth.le;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus;
import android.os.Parcel;
import android.os.Parcelable;
@@ -70,17 +73,21 @@
*/
private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
+
private final int mAdvertiseMode;
private final int mAdvertiseTxPowerLevel;
private final int mAdvertiseTimeoutMillis;
private final boolean mAdvertiseConnectable;
+ private final int mOwnAddressType;
private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
- boolean advertiseConnectable, int advertiseTimeout) {
+ boolean advertiseConnectable, int advertiseTimeout,
+ @AddressTypeStatus int ownAddressType) {
mAdvertiseMode = advertiseMode;
mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
mAdvertiseConnectable = advertiseConnectable;
mAdvertiseTimeoutMillis = advertiseTimeout;
+ mOwnAddressType = ownAddressType;
}
private AdvertiseSettings(Parcel in) {
@@ -88,6 +95,7 @@
mAdvertiseTxPowerLevel = in.readInt();
mAdvertiseConnectable = in.readInt() != 0;
mAdvertiseTimeoutMillis = in.readInt();
+ mOwnAddressType = in.readInt();
}
/**
@@ -118,12 +126,23 @@
return mAdvertiseTimeoutMillis;
}
+ /**
+ * @return the own address type for advertising
+ *
+ * @hide
+ */
+ @SystemApi
+ public @AddressTypeStatus int getOwnAddressType() {
+ return mOwnAddressType;
+ }
+
@Override
public String toString() {
return "Settings [mAdvertiseMode=" + mAdvertiseMode
+ ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel
+ ", mAdvertiseConnectable=" + mAdvertiseConnectable
- + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]";
+ + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis
+ + ", mOwnAddressType=" + mOwnAddressType + "]";
}
@Override
@@ -137,6 +156,7 @@
dest.writeInt(mAdvertiseTxPowerLevel);
dest.writeInt(mAdvertiseConnectable ? 1 : 0);
dest.writeInt(mAdvertiseTimeoutMillis);
+ dest.writeInt(mOwnAddressType);
}
public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR =
@@ -160,6 +180,7 @@
private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
private int mTimeoutMillis = 0;
private boolean mConnectable = true;
+ private int mOwnAddressType = AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT;
/**
* Set advertise mode to control the advertising power and latency.
@@ -226,10 +247,31 @@
}
/**
+ * Set own address type for advertising to control public or privacy mode. If used to set
+ * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
+ * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
+ * time of starting advertising.
+ *
+ * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
+ if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
+ || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
+ throw new IllegalArgumentException("unknown address type " + ownAddressType);
+ }
+ mOwnAddressType = ownAddressType;
+ return this;
+ }
+
+ /**
* Build the {@link AdvertiseSettings} object.
*/
public AdvertiseSettings build() {
- return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis);
+ return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis,
+ mOwnAddressType);
}
}
}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
index e39b198..5c8fae6 100644
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -16,11 +16,17 @@
package android.bluetooth.le;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The {@link AdvertisingSetParameters} provide a way to adjust advertising
* preferences for each
@@ -97,6 +103,39 @@
*/
private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
+ /** @hide */
+ @IntDef(prefix = "ADDRESS_TYPE_", value = {
+ ADDRESS_TYPE_DEFAULT,
+ ADDRESS_TYPE_PUBLIC,
+ ADDRESS_TYPE_RANDOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AddressTypeStatus {}
+
+ /**
+ * Advertise own address type that corresponds privacy settings of the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ADDRESS_TYPE_DEFAULT = -1;
+
+ /**
+ * Advertise own public address type.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ADDRESS_TYPE_PUBLIC = 0;
+
+ /**
+ * Generate and adverise own resolvable private address.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ADDRESS_TYPE_RANDOM = 1;
+
private final boolean mIsLegacy;
private final boolean mIsAnonymous;
private final boolean mIncludeTxPower;
@@ -106,11 +145,12 @@
private final boolean mScannable;
private final int mInterval;
private final int mTxPowerLevel;
+ private final int mOwnAddressType;
private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy,
boolean isAnonymous, boolean includeTxPower,
int primaryPhy, int secondaryPhy,
- int interval, int txPowerLevel) {
+ int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType) {
mConnectable = connectable;
mScannable = scannable;
mIsLegacy = isLegacy;
@@ -120,6 +160,7 @@
mSecondaryPhy = secondaryPhy;
mInterval = interval;
mTxPowerLevel = txPowerLevel;
+ mOwnAddressType = ownAddressType;
}
private AdvertisingSetParameters(Parcel in) {
@@ -132,6 +173,7 @@
mSecondaryPhy = in.readInt();
mInterval = in.readInt();
mTxPowerLevel = in.readInt();
+ mOwnAddressType = in.readInt();
}
/**
@@ -197,6 +239,16 @@
return mTxPowerLevel;
}
+ /**
+ * @return the own address type for advertising
+ *
+ * @hide
+ */
+ @SystemApi
+ public @AddressTypeStatus int getOwnAddressType() {
+ return mOwnAddressType;
+ }
+
@Override
public String toString() {
return "AdvertisingSetParameters [connectable=" + mConnectable
@@ -206,7 +258,8 @@
+ ", primaryPhy=" + mPrimaryPhy
+ ", secondaryPhy=" + mSecondaryPhy
+ ", interval=" + mInterval
- + ", txPowerLevel=" + mTxPowerLevel + "]";
+ + ", txPowerLevel=" + mTxPowerLevel
+ + ", ownAddressType=" + mOwnAddressType + "]";
}
@Override
@@ -225,6 +278,7 @@
dest.writeInt(mSecondaryPhy);
dest.writeInt(mInterval);
dest.writeInt(mTxPowerLevel);
+ dest.writeInt(mOwnAddressType);
}
public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR =
@@ -253,6 +307,7 @@
private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
private int mInterval = INTERVAL_LOW;
private int mTxPowerLevel = TX_POWER_MEDIUM;
+ private int mOwnAddressType = ADDRESS_TYPE_DEFAULT;
/**
* Set whether the advertisement type should be connectable or
@@ -399,6 +454,26 @@
}
/**
+ * Set own address type for advertising to control public or privacy mode. If used to set
+ * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
+ * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
+ * time of starting advertising.
+ *
+ * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
+ if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
+ || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
+ throw new IllegalArgumentException("unknown address type " + ownAddressType);
+ }
+ mOwnAddressType = ownAddressType;
+ return this;
+ }
+
+ /**
* Build the {@link AdvertisingSetParameters} object.
*
* @throws IllegalStateException if invalid combination of parameters is used.
@@ -431,7 +506,8 @@
}
return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous,
- mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel);
+ mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel,
+ mOwnAddressType);
}
}
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index b9f8a57..879dcee 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -138,6 +138,7 @@
parameters.setLegacyMode(true);
parameters.setConnectable(isConnectable);
parameters.setScannable(true); // legacy advertisements we support are always scannable
+ parameters.setOwnAddressType(settings.getOwnAddressType());
if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
parameters.setInterval(1600); // 1s
} else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9659df6..8a5e097 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4206,9 +4206,9 @@
* restored from (corresponds to {@link android.os.Build.VERSION#SDK_INT}). The first three
* values are represented as strings, the fourth one as int.
*
- * <p>This broadcast is sent only for settings provider entries known to require special handling
- * around restore time. These entries are found in the BROADCAST_ON_RESTORE table within
- * the provider's backup agent implementation.
+ * <p>This broadcast is sent only for settings provider entries known to require special
+ * handling around restore time to specific receivers. These entries are found in the
+ * BROADCAST_ON_RESTORE table within the provider's backup agent implementation.
*
* @see #EXTRA_SETTING_NAME
* @see #EXTRA_SETTING_PREVIOUS_VALUE
@@ -4216,15 +4216,46 @@
* @see #EXTRA_SETTING_RESTORED_FROM_SDK_INT
* {@hide}
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
- /** {@hide} */
+ /**
+ * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the name of the restored setting.
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_NAME = "setting_name";
- /** {@hide} */
+
+ /**
+ * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry prior to the restore
+ * operation.
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
- /** {@hide} */
+
+ /**
+ * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry being restored.
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
- /** {@hide} */
+
+ /**
+ * Int intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the version of the SDK that the setting has been restored from (corresponds to
+ * {@link android.os.Build.VERSION#SDK_INT}).
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
/**
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 1b5ab05..5554137 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -916,9 +916,23 @@
}
/**
- * Sets whether the local traffic is exempted from the VPN.
+ * Sets whether the local traffic is exempted from the VPN.
*
- * @hide TODO(184750836): unhide once the implementation is completed
+ * When this is set, the system will not use the VPN network when an app
+ * tries to send traffic for an IP address that is on a local network.
+ *
+ * Note that there are important security implications. In particular, the
+ * networks that the device connects to typically decides what IP addresses
+ * are part of the local network. This means that for VPNs setting this
+ * flag, it is possible for anybody to set up a public network in such a
+ * way that traffic to arbitrary IP addresses will bypass the VPN, including
+ * traffic to services like DNS. When using this API, please consider the
+ * security implications for your particular case.
+ *
+ * Note that because the local traffic will always bypass the VPN,
+ * it is not possible to set this flag on a non-bypassable VPN.
+ *
+ * @hide TODO(184750836): unhide once the implementation is completed
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/core/java/android/net/InternalNetworkUpdateRequest.java
index 6f09383..f42c4b7 100644
--- a/core/java/android/net/InternalNetworkUpdateRequest.java
+++ b/core/java/android/net/InternalNetworkUpdateRequest.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,7 +26,7 @@
public final class InternalNetworkUpdateRequest implements Parcelable {
@NonNull
private final StaticIpConfiguration mIpConfig;
- @Nullable
+ @NonNull
private final NetworkCapabilities mNetworkCapabilities;
@NonNull
@@ -37,20 +36,16 @@
@NonNull
public NetworkCapabilities getNetworkCapabilities() {
- return mNetworkCapabilities == null
- ? null : new NetworkCapabilities(mNetworkCapabilities);
+ return new NetworkCapabilities(mNetworkCapabilities);
}
/** @hide */
public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
- @Nullable final NetworkCapabilities networkCapabilities) {
+ @NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
+ Objects.requireNonNull(networkCapabilities);
mIpConfig = new StaticIpConfiguration(ipConfig);
- if (null == networkCapabilities) {
- mNetworkCapabilities = null;
- } else {
- mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
- }
+ mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2ced056..1ae1b05 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -41,6 +41,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.net.VpnConfig;
@@ -50,6 +51,7 @@
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -471,6 +473,13 @@
}
}
+ private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) {
+ final IpPrefix prefix = new IpPrefix(address, prefixLength);
+ if (!prefix.getAddress().equals(address)) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ }
+
/**
* Helper class to create a VPN interface. This class should be always
* used within the scope of the outer {@link VpnService}.
@@ -481,9 +490,9 @@
private final VpnConfig mConfig = new VpnConfig();
@UnsupportedAppUsage
- private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+ private final List<LinkAddress> mAddresses = new ArrayList<>();
@UnsupportedAppUsage
- private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+ private final List<RouteInfo> mRoutes = new ArrayList<>();
public Builder() {
mConfig.user = VpnService.this.getClass().getName();
@@ -555,7 +564,6 @@
throw new IllegalArgumentException("Bad address");
}
mAddresses.add(new LinkAddress(address, prefixLength));
- mConfig.updateAllowedFamilies(address);
return this;
}
@@ -579,28 +587,68 @@
* Add a network route to the VPN interface. Both IPv4 and IPv6
* routes are supported.
*
+ * If a route with the same destination is already present, its type will be updated.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ private Builder addRoute(@NonNull IpPrefix prefix, int type) {
+ check(prefix.getAddress(), prefix.getPrefixLength());
+
+ final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */
+ null, /* interface */ null, type);
+
+ final int index = findRouteIndexByDestination(newRoute);
+
+ if (index == -1) {
+ mRoutes.add(newRoute);
+ } else {
+ mRoutes.set(index, newRoute);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
* Adding a route implicitly allows traffic from that address family
* (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
*
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
* @throws IllegalArgumentException if the route is invalid.
*/
@NonNull
public Builder addRoute(@NonNull InetAddress address, int prefixLength) {
- check(address, prefixLength);
+ checkNonPrefixBytes(address, prefixLength);
- int offset = prefixLength / 8;
- byte[] bytes = address.getAddress();
- if (offset < bytes.length) {
- for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
- if (bytes[offset] != 0) {
- throw new IllegalArgumentException("Bad address");
- }
- }
- }
- mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null,
- RouteInfo.RTN_UNICAST));
- mConfig.updateAllowedFamilies(address);
- return this;
+ return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST);
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Adding a route implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder addRoute(@NonNull IpPrefix prefix) {
+ return addRoute(prefix, RouteInfo.RTN_UNICAST);
}
/**
@@ -611,6 +659,12 @@
* Adding a route implicitly allows traffic from that address family
* (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
*
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
* @throws IllegalArgumentException if the route is invalid.
* @see #addRoute(InetAddress, int)
*/
@@ -620,6 +674,23 @@
}
/**
+ * Exclude a network route from the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Calling this method overrides previous calls to {@link #addRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder excludeRoute(@NonNull IpPrefix prefix) {
+ return addRoute(prefix, RouteInfo.RTN_THROW);
+ }
+
+ /**
* Add a DNS server to the VPN connection. Both IPv4 and IPv6
* addresses are supported. If none is set, the DNS servers of
* the default network will be used.
@@ -900,5 +971,23 @@
throw new IllegalStateException(e);
}
}
+
+ private int findRouteIndexByDestination(RouteInfo route) {
+ for (int i = 0; i < mRoutes.size(); i++) {
+ if (mRoutes.get(i).getDestination().equals(route.getDestination())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Method for testing, to observe mRoutes while builder is being used.
+ * @hide
+ */
+ @VisibleForTesting
+ public List<RouteInfo> routes() {
+ return Collections.unmodifiableList(mRoutes);
+ }
}
}
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 1ac3f0a..125b573 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -15,6 +15,9 @@
*/
package android.net.vcn;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString;
+
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;
@@ -23,8 +26,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
import android.os.PersistableBundle;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
@@ -37,32 +44,36 @@
import java.util.Objects;
import java.util.Set;
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a configuration for a network template class of underlying cellular
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
@NonNull private final Set<String> mAllowedNetworkPlmnIds;
private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
- private static final String ALLOW_ROAMING_KEY = "mAllowRoaming";
- private final boolean mAllowRoaming;
+ private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria";
+ private final int mRoamingMatchCriteria;
- private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
- private final boolean mRequireOpportunistic;
+ private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria";
+ private final int mOpportunisticMatchCriteria;
private VcnCellUnderlyingNetworkTemplate(
int networkQuality,
- boolean allowMetered,
+ int meteredMatchCriteria,
Set<String> allowedNetworkPlmnIds,
Set<Integer> allowedSpecificCarrierIds,
- boolean allowRoaming,
- boolean requireOpportunistic) {
- super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered);
+ int roamingMatchCriteria,
+ int opportunisticMatchCriteria) {
+ super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, meteredMatchCriteria);
mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
- mAllowRoaming = allowRoaming;
- mRequireOpportunistic = requireOpportunistic;
+ mRoamingMatchCriteria = roamingMatchCriteria;
+ mOpportunisticMatchCriteria = opportunisticMatchCriteria;
validate();
}
@@ -72,15 +83,17 @@
protected void validate() {
super.validate();
validatePlmnIds(mAllowedNetworkPlmnIds);
- Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null");
+ Objects.requireNonNull(mAllowedSpecificCarrierIds, "matchingCarrierIds is null");
+ validateMatchCriteria(mRoamingMatchCriteria, "mRoamingMatchCriteria");
+ validateMatchCriteria(mOpportunisticMatchCriteria, "mOpportunisticMatchCriteria");
}
- private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) {
- Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null");
+ private static void validatePlmnIds(Set<String> matchingOperatorPlmnIds) {
+ Objects.requireNonNull(matchingOperatorPlmnIds, "matchingOperatorPlmnIds is null");
// A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal
// digits.
- for (String id : allowedNetworkPlmnIds) {
+ for (String id : matchingOperatorPlmnIds) {
if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) {
continue;
} else {
@@ -97,7 +110,7 @@
Objects.requireNonNull(in, "PersistableBundle is null");
final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
- final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
+ final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
final PersistableBundle plmnIdsBundle =
in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
@@ -114,16 +127,16 @@
PersistableBundleUtils.toList(
specificCarrierIdsBundle, INTEGER_DESERIALIZER));
- final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
- final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
+ final int roamingMatchCriteria = in.getInt(ROAMING_MATCH_KEY);
+ final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
return new VcnCellUnderlyingNetworkTemplate(
networkQuality,
- allowMetered,
+ meteredMatchCriteria,
allowedNetworkPlmnIds,
allowedSpecificCarrierIds,
- allowRoaming,
- requireOpportunistic);
+ roamingMatchCriteria,
+ opportunisticMatchCriteria);
}
/** @hide */
@@ -143,35 +156,51 @@
new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
- result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming);
- result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic);
+ result.putInt(ROAMING_MATCH_KEY, mRoamingMatchCriteria);
+ result.putInt(OPPORTUNISTIC_MATCH_KEY, mOpportunisticMatchCriteria);
return result;
}
- /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
+ /**
+ * Retrieve the matching operator PLMN IDs, or an empty set if any PLMN ID is acceptable.
+ *
+ * @see Builder#setOperatorPlmnIds(Set)
+ */
@NonNull
- public Set<String> getAllowedOperatorPlmnIds() {
+ public Set<String> getOperatorPlmnIds() {
return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
}
/**
- * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is
- * acceptable.
+ * Retrieve the matching sim specific carrier IDs, or an empty set if any sim specific carrier
+ * ID is acceptable.
+ *
+ * @see Builder#setSimSpecificCarrierIds(Set)
*/
@NonNull
- public Set<Integer> getAllowedSpecificCarrierIds() {
+ public Set<Integer> getSimSpecificCarrierIds() {
return Collections.unmodifiableSet(mAllowedSpecificCarrierIds);
}
- /** Return if roaming is allowed. */
- public boolean allowRoaming() {
- return mAllowRoaming;
+ /**
+ * Return the matching criteria for roaming networks.
+ *
+ * @see Builder#setRoaming(int)
+ */
+ @MatchCriteria
+ public int getRoaming() {
+ return mRoamingMatchCriteria;
}
- /** Return if requiring an opportunistic network. */
- public boolean requireOpportunistic() {
- return mRequireOpportunistic;
+ /**
+ * Return the matching criteria for opportunistic cellular subscriptions.
+ *
+ * @see Builder#setOpportunistic(int)
+ */
+ @MatchCriteria
+ public int getOpportunistic() {
+ return mOpportunisticMatchCriteria;
}
@Override
@@ -180,8 +209,8 @@
super.hashCode(),
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
- mAllowRoaming,
- mRequireOpportunistic);
+ mRoamingMatchCriteria,
+ mOpportunisticMatchCriteria);
}
@Override
@@ -197,8 +226,8 @@
final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
&& Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
- && mAllowRoaming == rhs.mAllowRoaming
- && mRequireOpportunistic == rhs.mRequireOpportunistic;
+ && mRoamingMatchCriteria == rhs.mRoamingMatchCriteria
+ && mOpportunisticMatchCriteria == rhs.mOpportunisticMatchCriteria;
}
/** @hide */
@@ -206,77 +235,137 @@
void dumpTransportSpecificFields(IndentingPrintWriter pw) {
pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
- pw.println("mAllowRoaming: " + mAllowRoaming);
- pw.println("mRequireOpportunistic: " + mRequireOpportunistic);
+ pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
+ pw.println(
+ "mOpportunisticMatchCriteria: "
+ + getMatchCriteriaString(mOpportunisticMatchCriteria));
}
- /** This class is used to incrementally build WifiNetworkPriority objects. */
- public static final class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
+ /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
+ public static final class Builder {
+ private int mNetworkQuality = NETWORK_QUALITY_ANY;
+ private int mMeteredMatchCriteria = MATCH_ANY;
+
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
- private boolean mAllowRoaming = false;
- private boolean mRequireOpportunistic = false;
+ private int mRoamingMatchCriteria = MATCH_ANY;
+ private int mOpportunisticMatchCriteria = MATCH_ANY;
/** Construct a Builder object. */
public Builder() {}
/**
- * Set allowed operator PLMN IDs.
+ * Set the required network quality to match this template.
+ *
+ * <p>Network quality is a aggregation of multiple signals that reflect the network link
+ * metrics. For example, the network validation bit (see {@link
+ * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
+ * and signal strength.
+ *
+ * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
+ * @hide
+ */
+ @NonNull
+ public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
+ validateNetworkQuality(networkQuality);
+
+ mNetworkQuality = networkQuality;
+ return this;
+ }
+
+ /**
+ * Set the matching criteria for metered networks.
+ *
+ * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+ * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+ * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+ *
+ * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+ */
+ // The matching getter is defined in the super class. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMetered()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setMetered(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setMetered");
+
+ mMeteredMatchCriteria = matchCriteria;
+ return this;
+ }
+
+ /**
+ * Set operator PLMN IDs with which a network can match this template.
*
* <p>This is used to distinguish cases where roaming agreements may dictate a different
* priority from a partner's networks.
*
- * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an
- * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and
- * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()}
- * and {@link SubscriptionInfo#getMncString()}.
+ * @param operatorPlmnIds the matching operator PLMN IDs in String. Network with one of the
+ * matching PLMN IDs can match this template. If the set is empty, any PLMN ID will
+ * match. The default is an empty set. A valid PLMN is a concatenation of MNC and MCC,
+ * and thus consists of 5 or 6 decimal digits.
+ * @see SubscriptionInfo#getMccString()
+ * @see SubscriptionInfo#getMncString()
*/
@NonNull
- public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
- validatePlmnIds(allowedNetworkPlmnIds);
+ public Builder setOperatorPlmnIds(@NonNull Set<String> operatorPlmnIds) {
+ validatePlmnIds(operatorPlmnIds);
mAllowedNetworkPlmnIds.clear();
- mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds);
+ mAllowedNetworkPlmnIds.addAll(operatorPlmnIds);
return this;
}
/**
- * Set allowed specific carrier IDs.
+ * Set sim specific carrier IDs with which a network can match this template.
*
- * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty
- * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}.
+ * @param simSpecificCarrierIds the matching sim specific carrier IDs. Network with one of
+ * the sim specific carrier IDs can match this template. If the set is empty, any
+ * carrier ID will match. The default is an empty set.
+ * @see TelephonyManager#getSimSpecificCarrierId()
*/
@NonNull
- public Builder setAllowedSpecificCarrierIds(
- @NonNull Set<Integer> allowedSpecificCarrierIds) {
- Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null");
+ public Builder setSimSpecificCarrierIds(@NonNull Set<Integer> simSpecificCarrierIds) {
+ Objects.requireNonNull(simSpecificCarrierIds, "simSpecificCarrierIds is null");
+
mAllowedSpecificCarrierIds.clear();
- mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds);
+ mAllowedSpecificCarrierIds.addAll(simSpecificCarrierIds);
return this;
}
/**
- * Set if roaming is allowed.
+ * Set the matching criteria for roaming networks.
*
- * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code
- * false}.
+ * <p>A template where setRoaming(MATCH_REQUIRED) will only match roaming networks (one
+ * without NET_CAPABILITY_NOT_ROAMING). A template where setRoaming(MATCH_FORBIDDEN) will
+ * only match a network that is not roaming (one with NET_CAPABILITY_NOT_ROAMING).
+ *
+ * @param matchCriteria the matching criteria for roaming networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING
*/
@NonNull
- public Builder setAllowRoaming(boolean allowRoaming) {
- mAllowRoaming = allowRoaming;
+ public Builder setRoaming(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setRoaming");
+
+ mRoamingMatchCriteria = matchCriteria;
return this;
}
/**
- * Set if requiring an opportunistic network.
+ * Set the matching criteria for opportunistic cellular subscriptions.
*
- * @param requireOpportunistic the flag to indicate if caller requires an opportunistic
- * network. Defaults to {@code false}.
+ * @param matchCriteria the matching criteria for opportunistic cellular subscriptions.
+ * Defaults to {@link #MATCH_ANY}.
+ * @see SubscriptionManager#setOpportunistic(boolean, int)
*/
@NonNull
- public Builder setRequireOpportunistic(boolean requireOpportunistic) {
- mRequireOpportunistic = requireOpportunistic;
+ public Builder setOpportunistic(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setOpportunistic");
+
+ mOpportunisticMatchCriteria = matchCriteria;
return this;
}
@@ -285,17 +374,11 @@
public VcnCellUnderlyingNetworkTemplate build() {
return new VcnCellUnderlyingNetworkTemplate(
mNetworkQuality,
- mAllowMetered,
+ mMeteredMatchCriteria,
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
- mAllowRoaming,
- mRequireOpportunistic);
- }
-
- /** @hide */
- @Override
- Builder self() {
- return this;
+ mRoamingMatchCriteria,
+ mOpportunisticMatchCriteria);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d07c24a..92956e8 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,6 +16,7 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -42,7 +43,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -162,30 +163,24 @@
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final LinkedHashSet<VcnUnderlyingNetworkTemplate>
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
+ public static final List<VcnUnderlyingNetworkTemplate> DEFAULT_UNDERLYING_NETWORK_TEMPLATES =
+ new ArrayList<>();
static {
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowRoaming(true /* allowRoaming */)
- .setRequireOpportunistic(true /* requireOpportunistic */)
+ .setOpportunistic(MATCH_REQUIRED)
.build());
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
.build());
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowRoaming(true /* allowRoaming */)
- .setRequireOpportunistic(false /* requireOpportunistic */)
.build());
}
@@ -200,9 +195,9 @@
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+ public static final String UNDERLYING_NETWORK_TEMPLATES_KEY = "mUnderlyingNetworkTemplates";
- @NonNull private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities;
+ @NonNull private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates;
private static final String MAX_MTU_KEY = "mMaxMtu";
private final int mMaxMtu;
@@ -215,7 +210,7 @@
@NonNull String gatewayConnectionName,
@NonNull IkeTunnelConnectionParams tunnelConnectionParams,
@NonNull Set<Integer> exposedCapabilities,
- @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
mGatewayConnectionName = gatewayConnectionName;
@@ -224,9 +219,9 @@
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
- mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities);
- if (mUnderlyingNetworkPriorities.isEmpty()) {
- mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
+ if (mUnderlyingNetworkTemplates.isEmpty()) {
+ mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
}
validate();
@@ -250,22 +245,19 @@
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- final PersistableBundle networkPrioritiesBundle =
- in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
+ final PersistableBundle networkTemplatesBundle =
+ in.getPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY);
- if (networkPrioritiesBundle == null) {
- // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus
+ if (networkTemplatesBundle == null) {
+ // UNDERLYING_NETWORK_TEMPLATES_KEY was added in Android T. Thus
// VcnGatewayConnectionConfig created on old platforms will not have this data and will
// be assigned with the default value
- mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
-
+ mUnderlyingNetworkTemplates = new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
} else {
- mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(
- PersistableBundleUtils.toList(
- networkPrioritiesBundle,
- VcnUnderlyingNetworkTemplate::fromPersistableBundle));
+ mUnderlyingNetworkTemplates =
+ PersistableBundleUtils.toList(
+ networkTemplatesBundle,
+ VcnUnderlyingNetworkTemplate::fromPersistableBundle);
}
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
@@ -285,7 +277,7 @@
checkValidCapability(cap);
}
- Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+ validateNetworkTemplateList(mUnderlyingNetworkTemplates);
Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
validateRetryInterval(mRetryIntervalsMs);
@@ -314,6 +306,19 @@
}
}
+ private static void validateNetworkTemplateList(
+ List<VcnUnderlyingNetworkTemplate> networkPriorityRules) {
+ Objects.requireNonNull(networkPriorityRules, "networkPriorityRules is null");
+
+ Set<VcnUnderlyingNetworkTemplate> existingRules = new ArraySet<>();
+ for (VcnUnderlyingNetworkTemplate rule : networkPriorityRules) {
+ Objects.requireNonNull(rule, "Found null value VcnUnderlyingNetworkTemplate");
+ if (!existingRules.add(rule)) {
+ throw new IllegalArgumentException("Found duplicate VcnUnderlyingNetworkTemplate");
+ }
+ }
+ }
+
/**
* Returns the configured Gateway Connection name.
*
@@ -368,15 +373,13 @@
}
/**
- * Retrieve the configured VcnUnderlyingNetworkTemplate list, or a default list if it is not
- * configured.
+ * Retrieve the VcnUnderlyingNetworkTemplate list, or a default list if it is not configured.
*
- * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkTemplate>)
- * @hide
+ * @see Builder#setVcnUnderlyingNetworkPriorities(List)
*/
@NonNull
- public LinkedHashSet<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
- return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
+ public List<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
+ return new ArrayList<>(mUnderlyingNetworkTemplates);
}
/**
@@ -415,15 +418,15 @@
PersistableBundleUtils.fromList(
new ArrayList<>(mExposedCapabilities),
PersistableBundleUtils.INTEGER_SERIALIZER);
- final PersistableBundle networkPrioritiesBundle =
+ final PersistableBundle networkTemplatesBundle =
PersistableBundleUtils.fromList(
- new ArrayList<>(mUnderlyingNetworkPriorities),
+ mUnderlyingNetworkTemplates,
VcnUnderlyingNetworkTemplate::toPersistableBundle);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
- result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle);
+ result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
@@ -436,7 +439,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
- mUnderlyingNetworkPriorities,
+ mUnderlyingNetworkTemplates,
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu);
}
@@ -451,7 +454,7 @@
return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
&& mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
- && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities)
+ && mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
}
@@ -465,8 +468,8 @@
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull
- private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates =
+ new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
@@ -539,27 +542,37 @@
}
/**
- * Set the VcnUnderlyingNetworkTemplate list.
+ * Set the list of templates to match underlying networks against, in high-to-low priority
+ * order.
*
- * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
- * are ordered from most to least preferred, or an empty list to use the default
- * prioritization. The default network prioritization is Opportunistic cellular, Carrier
- * WiFi and Macro cellular
- * @return
+ * <p>To select the VCN underlying network, the VCN connection will go through all the
+ * network candidates and return a network matching the highest priority rule.
+ *
+ * <p>If multiple networks match the same rule, the VCN will prefer an already-selected
+ * network as opposed to a new/unselected network. However, if both are new/unselected
+ * networks, a network will be chosen arbitrarily amongst the networks matching the highest
+ * priority rule.
+ *
+ * <p>If all networks fail to match the rules provided, an underlying network will still be
+ * selected (at random if necessary).
+ *
+ * @param underlyingNetworkTemplates a list of unique VcnUnderlyingNetworkTemplates that are
+ * ordered from most to least preferred, or an empty list to use the default
+ * prioritization. The default network prioritization order is Opportunistic cellular,
+ * Carrier WiFi and then Macro cellular.
+ * @return this {@link Builder} instance, for chaining
*/
- /** @hide */
@NonNull
public Builder setVcnUnderlyingNetworkPriorities(
- @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities) {
- Objects.requireNonNull(
- mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+ validateNetworkTemplateList(underlyingNetworkTemplates);
- mUnderlyingNetworkPriorities.clear();
+ mUnderlyingNetworkTemplates.clear();
- if (underlyingNetworkPriorities.isEmpty()) {
- mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ if (underlyingNetworkTemplates.isEmpty()) {
+ mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
} else {
- mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities);
+ mUnderlyingNetworkTemplates.addAll(underlyingNetworkTemplates);
}
return this;
@@ -629,7 +642,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
- mUnderlyingNetworkPriorities,
+ mUnderlyingNetworkTemplates,
mRetryIntervalsMs,
mMaxMtu);
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index d306d5c..60fc936 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -15,6 +15,8 @@
*/
package android.net.vcn;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
@@ -31,17 +33,24 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a template containing set of underlying network requirements for doing
+ * route selection.
+ *
+ * <p>Apps provisioning a VCN can configure the underlying network priority for each Gateway
+ * Connection by setting a list (in priority order, most to least preferred) of the appropriate
+ * subclasses in the VcnGatewayConnectionConfig. See {@link
+ * VcnGatewayConnectionConfig.Builder#setVcnUnderlyingNetworkPriorities}
+ */
public abstract class VcnUnderlyingNetworkTemplate {
/** @hide */
- protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
+ static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
/** @hide */
- protected static final int NETWORK_PRIORITY_TYPE_CELL = 2;
+ static final int NETWORK_PRIORITY_TYPE_CELL = 2;
- /** Denotes that any network quality is acceptable */
+ /** Denotes that any network quality is acceptable. @hide */
public static final int NETWORK_QUALITY_ANY = 0;
- /** Denotes that network quality needs to be OK */
+ /** Denotes that network quality needs to be OK. @hide */
public static final int NETWORK_QUALITY_OK = 100000;
private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>();
@@ -56,34 +65,82 @@
@IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
public @interface NetworkQuality {}
+ /**
+ * Used to configure the matching criteria of a network characteristic. This may include network
+ * capabilities, or cellular subscription information. Denotes that networks with or without the
+ * characteristic are both acceptable to match this template.
+ */
+ public static final int MATCH_ANY = 0;
+
+ /**
+ * Used to configure the matching criteria of a network characteristic. This may include network
+ * capabilities, or cellular subscription information. Denotes that a network MUST have the
+ * capability in order to match this template.
+ */
+ public static final int MATCH_REQUIRED = 1;
+
+ /**
+ * Used to configure the matching criteria of a network characteristic. This may include network
+ * capabilities, or cellular subscription information. Denotes that a network MUST NOT have the
+ * capability in order to match this template.
+ */
+ public static final int MATCH_FORBIDDEN = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MATCH_ANY, MATCH_REQUIRED, MATCH_FORBIDDEN})
+ public @interface MatchCriteria {}
+
+ private static final SparseArray<String> MATCH_CRITERIA_TO_STRING_MAP = new SparseArray<>();
+
+ static {
+ MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_ANY, "MATCH_ANY");
+ MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_REQUIRED, "MATCH_REQUIRED");
+ MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_FORBIDDEN, "MATCH_FORBIDDEN");
+ }
+
private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType";
private final int mNetworkPriorityType;
/** @hide */
- protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
+ static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
+
private final int mNetworkQuality;
/** @hide */
- protected static final String ALLOW_METERED_KEY = "mAllowMetered";
- private final boolean mAllowMetered;
+ static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
+
+ private final int mMeteredMatchCriteria;
/** @hide */
- protected VcnUnderlyingNetworkTemplate(
- int networkPriorityType, int networkQuality, boolean allowMetered) {
+ VcnUnderlyingNetworkTemplate(
+ int networkPriorityType, int networkQuality, int meteredMatchCriteria) {
mNetworkPriorityType = networkPriorityType;
mNetworkQuality = networkQuality;
- mAllowMetered = allowMetered;
+ mMeteredMatchCriteria = meteredMatchCriteria;
}
- private static void validateNetworkQuality(int networkQuality) {
+ /** @hide */
+ static void validateNetworkQuality(int networkQuality) {
Preconditions.checkArgument(
networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
"Invalid networkQuality:" + networkQuality);
}
/** @hide */
+ static void validateMatchCriteria(int meteredMatchCriteria, String matchingCapability) {
+ Preconditions.checkArgument(
+ MATCH_CRITERIA_TO_STRING_MAP.contains(meteredMatchCriteria),
+ "Invalid matching criteria: "
+ + meteredMatchCriteria
+ + " for "
+ + matchingCapability);
+ }
+
+ /** @hide */
protected void validate() {
validateNetworkQuality(mNetworkQuality);
+ validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
}
/** @hide */
@@ -112,14 +169,14 @@
result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
- result.putBoolean(ALLOW_METERED_KEY, mAllowMetered);
+ result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
return result;
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered);
+ return Objects.hash(mNetworkPriorityType, mNetworkQuality, mMeteredMatchCriteria);
}
@Override
@@ -131,7 +188,17 @@
final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
return mNetworkPriorityType == rhs.mNetworkPriorityType
&& mNetworkQuality == rhs.mNetworkQuality
- && mAllowMetered == rhs.mAllowMetered;
+ && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria;
+ }
+
+ /** @hide */
+ static String getNameString(SparseArray<String> toStringMap, int key) {
+ return toStringMap.get(key, "Invalid value " + key);
+ }
+
+ /** @hide */
+ static String getMatchCriteriaString(int meteredMatchCriteria) {
+ return getNameString(MATCH_CRITERIA_TO_STRING_MAP, meteredMatchCriteria);
}
/** @hide */
@@ -148,65 +215,32 @@
pw.println(
"mNetworkQuality: "
- + NETWORK_QUALITY_TO_STRING_MAP.get(
- mNetworkQuality, "Invalid value " + mNetworkQuality));
- pw.println("mAllowMetered: " + mAllowMetered);
+ + getNameString(NETWORK_QUALITY_TO_STRING_MAP, mNetworkQuality));
+ pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
dumpTransportSpecificFields(pw);
pw.decreaseIndent();
}
- /** Retrieve the required network quality. */
+ /**
+ * Retrieve the required network quality to match this template.
+ *
+ * @see Builder#setNetworkQuality(int)
+ * @hide
+ */
@NetworkQuality
public int getNetworkQuality() {
return mNetworkQuality;
}
- /** Return if a metered network is allowed. */
- public boolean allowMetered() {
- return mAllowMetered;
- }
-
/**
- * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects.
+ * Return the matching criteria for metered networks.
*
- * @param <T> The subclass to be built.
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
*/
- public abstract static class Builder<T extends Builder<T>> {
- /** @hide */
- protected int mNetworkQuality = NETWORK_QUALITY_ANY;
- /** @hide */
- protected boolean mAllowMetered = false;
-
- /** @hide */
- protected Builder() {}
-
- /**
- * Set the required network quality.
- *
- * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
- */
- @NonNull
- public T setNetworkQuality(@NetworkQuality int networkQuality) {
- validateNetworkQuality(networkQuality);
-
- mNetworkQuality = networkQuality;
- return self();
- }
-
- /**
- * Set if a metered network is allowed.
- *
- * @param allowMetered the flag to indicate if a metered network is allowed, defaults to
- * {@code false}
- */
- @NonNull
- public T setAllowMetered(boolean allowMetered) {
- mAllowMetered = allowMetered;
- return self();
- }
-
- /** @hide */
- abstract T self();
+ @MatchCriteria
+ public int getMetered() {
+ return mMeteredMatchCriteria;
}
}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 6bbb2bf..272ca9d 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -16,31 +16,59 @@
package android.net.vcn;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
import android.os.PersistableBundle;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a configuration for a network template class of underlying Carrier WiFi
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
- private static final String SSID_KEY = "mSsid";
- @Nullable private final String mSsid;
+ private static final String SSIDS_KEY = "mSsids";
+ @Nullable private final Set<String> mSsids;
private VcnWifiUnderlyingNetworkTemplate(
- int networkQuality, boolean allowMetered, String ssid) {
- super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
- mSsid = ssid;
+ int networkQuality, int meteredMatchCriteria, Set<String> ssids) {
+ super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, meteredMatchCriteria);
+ mSsids = new ArraySet<>(ssids);
validate();
}
/** @hide */
+ @Override
+ protected void validate() {
+ super.validate();
+ validateSsids(mSsids);
+ }
+
+ private static void validateSsids(Set<String> ssids) {
+ Objects.requireNonNull(ssids, "ssids is null");
+
+ for (String ssid : ssids) {
+ Objects.requireNonNull(ssid, "found null value ssid");
+ }
+ }
+
+ /** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle(
@@ -48,9 +76,14 @@
Objects.requireNonNull(in, "PersistableBundle is null");
final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
- final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
- final String ssid = in.getString(SSID_KEY);
- return new VcnWifiUnderlyingNetworkTemplate(networkQuality, allowMetered, ssid);
+ final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+
+ final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY);
+ Objects.requireNonNull(ssidsBundle, "ssidsBundle is null");
+ final Set<String> ssids =
+ new ArraySet<String>(
+ PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER));
+ return new VcnWifiUnderlyingNetworkTemplate(networkQuality, meteredMatchCriteria, ssids);
}
/** @hide */
@@ -59,13 +92,17 @@
@VisibleForTesting(visibility = Visibility.PROTECTED)
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = super.toPersistableBundle();
- result.putString(SSID_KEY, mSsid);
+
+ final PersistableBundle ssidsBundle =
+ PersistableBundleUtils.fromList(new ArrayList<>(mSsids), STRING_SERIALIZER);
+ result.putPersistableBundle(SSIDS_KEY, ssidsBundle);
+
return result;
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), mSsid);
+ return Objects.hash(super.hashCode(), mSsids);
}
@Override
@@ -79,49 +116,95 @@
}
final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other;
- return mSsid.equals(rhs.mSsid);
+ return mSsids.equals(rhs.mSsids);
}
/** @hide */
@Override
void dumpTransportSpecificFields(IndentingPrintWriter pw) {
- pw.println("mSsid: " + mSsid);
+ pw.println("mSsids: " + mSsids);
}
- /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */
- @Nullable
- public String getSsid() {
- return mSsid;
+ /**
+ * Retrieve the matching SSIDs, or an empty set if any SSID is acceptable.
+ *
+ * @see Builder#setSsids(Set)
+ */
+ @NonNull
+ public Set<String> getSsids() {
+ return Collections.unmodifiableSet(mSsids);
}
/** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
- public static class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
- @Nullable private String mSsid;
+ public static final class Builder {
+ private int mNetworkQuality = NETWORK_QUALITY_ANY;
+ private int mMeteredMatchCriteria = MATCH_ANY;
+ @NonNull private final Set<String> mSsids = new ArraySet<>();
/** Construct a Builder object. */
public Builder() {}
/**
- * Set the required SSID.
+ * Set the required network quality to match this template.
*
- * @param ssid the required SSID, or {@code null} if any SSID is acceptable.
+ * <p>Network quality is a aggregation of multiple signals that reflect the network link
+ * metrics. For example, the network validation bit (see {@link
+ * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
+ * and signal strength.
+ *
+ * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
+ * @hide
*/
@NonNull
- public Builder setSsid(@Nullable String ssid) {
- mSsid = ssid;
+ public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
+ validateNetworkQuality(networkQuality);
+
+ mNetworkQuality = networkQuality;
+ return this;
+ }
+
+ /**
+ * Set the matching criteria for metered networks.
+ *
+ * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+ * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+ * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+ *
+ * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+ */
+ // The matching getter is defined in the super class. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMetered()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setMetered(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setMetered");
+
+ mMeteredMatchCriteria = matchCriteria;
+ return this;
+ }
+
+ /**
+ * Set the SSIDs with which a network can match this priority rule.
+ *
+ * @param ssids the matching SSIDs. Network with one of the matching SSIDs can match this
+ * priority rule. If the set is empty, any SSID will match. The default is an empty set.
+ */
+ @NonNull
+ public Builder setSsids(@NonNull Set<String> ssids) {
+ validateSsids(ssids);
+
+ mSsids.clear();
+ mSsids.addAll(ssids);
return this;
}
/** Build the VcnWifiUnderlyingNetworkTemplate. */
@NonNull
public VcnWifiUnderlyingNetworkTemplate build() {
- return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid);
- }
-
- /** @hide */
- @Override
- Builder self() {
- return this;
+ return new VcnWifiUnderlyingNetworkTemplate(
+ mNetworkQuality, mMeteredMatchCriteria, mSsids);
}
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9eaaa91..542de3f 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -131,6 +131,7 @@
mContext.getAttributionTag(), callback);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -152,6 +153,7 @@
mSubscriptionChangedListenerMap.remove(listener);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -194,6 +196,7 @@
mContext.getAttributionTag(), callback);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -216,6 +219,7 @@
mOpportunisticSubscriptionChangedListenerMap.remove(listener);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -304,6 +308,7 @@
sRegistry.notifyCarrierNetworkChange(active);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -329,6 +334,7 @@
sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -347,6 +353,7 @@
sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -364,6 +371,7 @@
sRegistry.notifyCallStateForAllSubs(state, incomingNumber);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -376,6 +384,7 @@
sRegistry.notifySubscriptionInfoChanged();
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -388,6 +397,7 @@
sRegistry.notifyOpportunisticSubscriptionInfoChanged();
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -404,6 +414,7 @@
sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -421,6 +432,7 @@
sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -439,6 +451,7 @@
sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -454,6 +467,7 @@
sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -469,6 +483,7 @@
sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -490,6 +505,7 @@
slotIndex, subId, preciseState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -508,6 +524,7 @@
sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -523,6 +540,7 @@
sRegistry.notifyEmergencyNumberList(slotIndex, subId);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -538,6 +556,7 @@
sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -553,6 +572,7 @@
sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -570,6 +590,7 @@
sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -583,6 +604,7 @@
sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -615,6 +637,7 @@
SIM_ACTIVATION_TYPE_DATA, activationState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -634,6 +657,7 @@
SIM_ACTIVATION_TYPE_VOICE, activationState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -651,6 +675,7 @@
sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -669,6 +694,7 @@
sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -683,6 +709,7 @@
sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -698,6 +725,7 @@
sRegistry.notifySrvccStateChanged(subId, state);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -721,6 +749,7 @@
foregroundCallPreciseState, backgroundCallPreciseState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -741,6 +770,7 @@
sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -755,6 +785,7 @@
sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -769,7 +800,7 @@
try {
sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
} catch (RemoteException ex) {
-
+ throw ex.rethrowFromSystemServer();
}
}
@@ -781,7 +812,7 @@
try {
sRegistry.notifyActiveDataSubIdChanged(activeDataSubId);
} catch (RemoteException ex) {
-
+ throw ex.rethrowFromSystemServer();
}
}
@@ -814,6 +845,7 @@
sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
chosenPlmn, domain, causeCode, additionalCauseCode);
} catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -830,6 +862,7 @@
sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -846,6 +879,7 @@
sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -862,6 +896,7 @@
sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -880,6 +915,7 @@
allowedNetworkType);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -895,6 +931,7 @@
sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 2e7629a..2ae56f8 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -34,8 +34,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
-import java.net.Inet4Address;
-import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -93,8 +91,8 @@
public String interfaze;
public String session;
public int mtu = -1;
- public List<LinkAddress> addresses = new ArrayList<LinkAddress>();
- public List<RouteInfo> routes = new ArrayList<RouteInfo>();
+ public List<LinkAddress> addresses = new ArrayList<>();
+ public List<RouteInfo> routes = new ArrayList<>();
public List<String> dnsServers;
public List<String> searchDomains;
public List<String> allowedApplications;
@@ -114,12 +112,32 @@
public VpnConfig() {
}
- public void updateAllowedFamilies(InetAddress address) {
- if (address instanceof Inet4Address) {
- allowIPv4 = true;
- } else {
- allowIPv6 = true;
- }
+ public VpnConfig(VpnConfig other) {
+ user = other.user;
+ interfaze = other.interfaze;
+ session = other.session;
+ mtu = other.mtu;
+ addresses = copyOf(other.addresses);
+ routes = copyOf(other.routes);
+ dnsServers = copyOf(other.dnsServers);
+ searchDomains = copyOf(other.searchDomains);
+ allowedApplications = copyOf(other.allowedApplications);
+ disallowedApplications = copyOf(other.disallowedApplications);
+ configureIntent = other.configureIntent;
+ startTime = other.startTime;
+ legacy = other.legacy;
+ blocking = other.blocking;
+ allowBypass = other.allowBypass;
+ allowIPv4 = other.allowIPv4;
+ allowIPv6 = other.allowIPv6;
+ isMetered = other.isMetered;
+ underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
+ other.underlyingNetworks, other.underlyingNetworks.length) : null;
+ proxyInfo = other.proxyInfo;
+ }
+
+ private static <T> List<T> copyOf(List<T> list) {
+ return list != null ? new ArrayList<>(list) : null;
}
public void addLegacyRoutes(String routesStr) {
@@ -131,7 +149,6 @@
//each route is ip/prefix
RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST);
this.routes.add(info);
- updateAllowedFamilies(info.getDestination().getAddress());
}
}
@@ -144,7 +161,6 @@
//each address is ip/prefix
LinkAddress addr = new LinkAddress(address);
this.addresses.add(addr);
- updateAllowedFamilies(addr.getAddress());
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0f1c6f3..270b5d6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
import android.compat.annotation.UnsupportedAppUsage;
@@ -36,7 +37,6 @@
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.location.GnssSignalQuality;
-import android.net.INetworkStatsService;
import android.net.NetworkStats;
import android.net.Uri;
import android.net.wifi.WifiManager;
@@ -136,7 +136,9 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Queue;
+import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
@@ -11539,19 +11541,11 @@
private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
@VisibleForTesting
- protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
- try {
- if (!ArrayUtils.isEmpty(ifaces)) {
- INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- if (statsService != null) {
- return statsService.getDetailedUidStats(ifaces);
- } else {
- Slog.e(TAG, "Failed to get networkStatsService ");
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e);
+ protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
+ String[] ifaces) {
+ Objects.requireNonNull(networkStatsManager);
+ if (!ArrayUtils.isEmpty(ifaces)) {
+ return networkStatsManager.getDetailedUidStats(Set.of(ifaces));
}
return null;
}
@@ -11561,7 +11555,8 @@
* @param info The energy information from the WiFi controller.
*/
public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
- final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+ @NonNull NetworkStatsManager networkStatsManager) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
}
@@ -11569,7 +11564,8 @@
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mWifiNetworkLock) {
- final NetworkStats latestStats = readNetworkStatsLocked(mWifiIfaces);
+ final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
+ mWifiIfaces);
if (latestStats != null) {
delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null,
mNetworkStatsPool.acquire());
@@ -11921,7 +11917,8 @@
* Distribute Cell radio energy info and network traffic to apps.
*/
public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
- final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+ @NonNull NetworkStatsManager networkStatsManager) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
@@ -11935,7 +11932,8 @@
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
- final NetworkStats latestStats = readNetworkStatsLocked(mModemIfaces);
+ final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
+ mModemIfaces);
if (latestStats != null) {
delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null,
mNetworkStatsPool.acquire());
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2cfec4b..07b16ed 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -242,6 +242,7 @@
"audioflinger-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
+ "libandroid_net",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index afad08a..1be1873 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -15,24 +15,23 @@
*/
#define LOG_TAG "NMST_QTagUidNative"
-#include <utils/Log.h>
-#include <nativehelper/JNIPlatformHelp.h>
-
-#include "jni.h"
-#include <utils/misc.h>
+#include <android/multinetwork.h>
#include <cutils/qtaguid.h>
-
#include <errno.h>
#include <fcntl.h>
-#include <sys/types.h>
+#include <nativehelper/JNIPlatformHelp.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "jni.h"
namespace android {
-static jint QTagUid_tagSocketFd(JNIEnv* env, jclass,
- jobject fileDescriptor,
- jint tagNum, jint uid) {
+static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor,
+ jint tagNum, jint uid) {
int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
@@ -40,15 +39,14 @@
return (jint)-1;
}
- int res = qtaguid_tagSocket(userFd, tagNum, uid);
+ int res = android_tag_socket_with_uid(userFd, tagNum, uid);
if (res < 0) {
return (jint)-errno;
}
return (jint)res;
}
-static jint QTagUid_untagSocketFd(JNIEnv* env, jclass,
- jobject fileDescriptor) {
+static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
@@ -56,16 +54,14 @@
return (jint)-1;
}
- int res = qtaguid_untagSocket(userFd);
+ int res = android_untag_socket(userFd);
if (res < 0) {
return (jint)-errno;
}
return (jint)res;
}
-static jint QTagUid_setCounterSet(JNIEnv* env, jclass,
- jint setNum, jint uid) {
-
+static jint setCounterSet(JNIEnv* env, jclass, jint setNum, jint uid) {
int res = qtaguid_setCounterSet(setNum, uid);
if (res < 0) {
return (jint)-errno;
@@ -73,9 +69,7 @@
return (jint)res;
}
-static jint QTagUid_deleteTagData(JNIEnv* env, jclass,
- jint tagNum, jint uid) {
-
+static jint deleteTagData(JNIEnv* env, jclass, jint tagNum, jint uid) {
int res = qtaguid_deleteTagData(tagNum, uid);
if (res < 0) {
return (jint)-errno;
@@ -84,10 +78,10 @@
}
static const JNINativeMethod gQTagUidMethods[] = {
- { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
- { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
- { "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
- { "native_deleteTagData", "(II)I", (void*)QTagUid_deleteTagData},
+ { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd},
+ { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd},
+ { "native_setCounterSet", "(II)I", (void*)setCounterSet},
+ { "native_deleteTagData", "(II)I", (void*)deleteTagData},
};
int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6ecfcec..591cf81 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2114,8 +2114,9 @@
<permission android:name="android.permission.NFC_HANDOVER_STATUS"
android:protectionLevel="signature|privileged" />
- <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
- <p>Not for use by third-party applications. -->
+ <!-- @SystemApi Allows internal management of Bluetooth state when on wireless consent mode.
+ <p>Not for use by third-party applications.
+ @hide -->
<permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 48a1da1..1cc1894 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.usage.NetworkStatsManager;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
@@ -40,12 +41,15 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MobileRadioPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ @Mock
+ NetworkStatsManager mNetworkStatsManager;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -90,7 +94,8 @@
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
new int[] {100, 200, 300, 400, 500}, 600);
- stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
+ stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000,
+ mNetworkStatsManager);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -150,7 +155,7 @@
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
new int[] {100, 200, 300, 400, 500}, 600);
- stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
+ stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000, mNetworkStatsManager);
mStatsRule.setTime(12_000_000, 12_000_000);
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index cee1a03..db0c934 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
import android.net.NetworkStats;
import android.os.Handler;
import android.os.Looper;
@@ -105,7 +107,8 @@
}
@Override
- protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
+ protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
+ String[] ifaces) {
return mNetworkStats;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index fc44ddc..e7ce9a0 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.usage.NetworkStatsManager;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
@@ -35,6 +36,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -43,6 +45,9 @@
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ @Mock
+ NetworkStatsManager mNetworkStatsManager;
+
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
@@ -80,7 +85,8 @@
final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000);
+ batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000,
+ mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -113,7 +119,7 @@
final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000);
+ batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000, mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
@@ -160,7 +166,8 @@
// Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
// on the packet counts.
- batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000);
+ batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000,
+ mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -180,7 +187,8 @@
// Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
// on the packet counts.
- batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000);
+ batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000,
+ mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 6942017..3ca457f 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -67,7 +67,7 @@
size_t chunkSize = env->GetArrayLength(obj);
if (chunkSize < (int) (sizeof(Res_png_9patch))) {
jniThrowRuntimeException(env, "Array too small for chunk.");
- return NULL;
+ return 0;
}
int8_t* storage = new int8_t[chunkSize];
diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp
index 72995ef..8cbb70e 100644
--- a/libs/hwui/jni/android_util_PathParser.cpp
+++ b/libs/hwui/jni/android_util_PathParser.cpp
@@ -61,7 +61,7 @@
} else {
delete pathData;
doThrowIAE(env, result.failureMessage.c_str());
- return NULL;
+ return 0;
}
}
diff --git a/media/OWNERS b/media/OWNERS
index 0aff43e..5f50137 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -3,7 +3,6 @@
essick@google.com
etalvala@google.com
hdmoon@google.com
-hkuang@google.com
hunga@google.com
insun@google.com
jaewan@google.com
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 773cdc9..50bb79c 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -275,118 +275,104 @@
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
std::vector<std::any>& garbage)
{
- // oldTrack and newTrack are placeholders to be released by garbage without the lock.
- sp<AudioTrack> oldTrack;
- sp<AudioTrack> newTrack;
- status_t status = NO_ERROR;
+ ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
+ " priority=%d, loop=%d, rate=%f)",
+ __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
+ priority, loop, rate);
- {
- ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
- " priority=%d, loop=%d, rate=%f)",
- __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
- priority, loop, rate);
+ // initialize track
+ const audio_stream_type_t streamType =
+ AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
+ const int32_t channelCount = sound->getChannelCount();
+ const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
+ size_t frameCount = 0;
- // initialize track
- const audio_stream_type_t streamType =
- AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
- const int32_t channelCount = sound->getChannelCount();
- const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
- size_t frameCount = 0;
+ if (loop) {
+ const audio_format_t format = sound->getFormat();
+ const size_t frameSize = audio_is_linear_pcm(format)
+ ? channelCount * audio_bytes_per_sample(format) : 1;
+ frameCount = sound->getSizeInBytes() / frameSize;
+ }
- if (loop) {
- const audio_format_t format = sound->getFormat();
- const size_t frameSize = audio_is_linear_pcm(format)
- ? channelCount * audio_bytes_per_sample(format) : 1;
- frameCount = sound->getSizeInBytes() / frameSize;
- }
-
- // check if the existing track has the same sound id.
- if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
+ if (mAudioTrack != nullptr) {
+ if (mSoundID == sound->getSoundID()
+ && mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+ // Reuse the old track if the soundID matches.
// the sample rate may fail to change if the audio track is a fast track.
- if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
- newTrack = mAudioTrack;
- ALOGV("%s: reusing track %p for sound %d",
- __func__, mAudioTrack.get(), sound->getSoundID());
- }
- }
- if (newTrack == nullptr) {
- // mToggle toggles each time a track is started on a given stream.
- // The toggle is concatenated with the Stream address and passed to AudioTrack
- // as callback user data. This enables the detection of callbacks received from the old
- // audio track while the new one is being started and avoids processing them with
- // wrong audio audio buffer size (mAudioBufferSize)
- auto toggle = mToggle ^ 1;
- // NOLINTNEXTLINE(performance-no-int-to-ptr)
- void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
- audio_channel_mask_t soundChannelMask = sound->getChannelMask();
- // When sound contains a valid channel mask, use it as is.
- // Otherwise, use stream count to calculate channel mask.
- audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
- ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
-
- // do not create a new audio track if current track is compatible with sound parameters
-
- android::content::AttributionSourceState attributionSource;
- attributionSource.packageName = mStreamManager->getOpPackageName();
- attributionSource.token = sp<BBinder>::make();
- // TODO b/182469354 make consistent with AudioRecord, add util for native source
- newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
- channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
- staticCallback, userData,
- 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
- AudioTrack::TRANSFER_DEFAULT,
- nullptr /*offloadInfo*/, attributionSource,
- mStreamManager->getAttributes(),
- false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
- // Set caller name so it can be logged in destructor.
- // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
- newTrack->setCallerName("soundpool");
- oldTrack = mAudioTrack;
- status = newTrack->initCheck();
- if (status != NO_ERROR) {
- ALOGE("%s: error creating AudioTrack", __func__);
- // newTrack goes out of scope, so reference count drops to zero
- goto exit;
- }
- // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
- mToggle = toggle;
- mAudioTrack = newTrack;
- ALOGV("%s: using new track %p for sound %d",
- __func__, newTrack.get(), sound->getSoundID());
- }
- if (mMuted) {
- newTrack->setVolume(0.0f, 0.0f);
+ ALOGV("%s: reusing track %p for sound %d",
+ __func__, mAudioTrack.get(), sound->getSoundID());
} else {
- newTrack->setVolume(leftVolume, rightVolume);
+ // If reuse not possible, move mAudioTrack to garbage, set to nullptr.
+ garbage.emplace_back(std::move(mAudioTrack));
+ mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
}
- newTrack->setLoop(0, frameCount, loop);
- mAudioTrack->start();
- mSound = sound;
- mSoundID = sound->getSoundID();
- mPriority = priority;
- mLoop = loop;
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- mRate = rate;
- mState = PLAYING;
- mStopTimeNs = 0;
- mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
}
+ if (mAudioTrack == nullptr) {
+ // mToggle toggles each time a track is started on a given stream.
+ // The toggle is concatenated with the Stream address and passed to AudioTrack
+ // as callback user data. This enables the detection of callbacks received from the old
+ // audio track while the new one is being started and avoids processing them with
+ // wrong audio audio buffer size (mAudioBufferSize)
+ auto toggle = mToggle ^ 1;
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
+ audio_channel_mask_t soundChannelMask = sound->getChannelMask();
+ // When sound contains a valid channel mask, use it as is.
+ // Otherwise, use stream count to calculate channel mask.
+ audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
+ ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
-exit:
- ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
- if (status != NO_ERROR) {
- // TODO: should we consider keeping the soundID if the old track is OK?
- // Do not attempt to restart this track (should we remove the stream id?)
- mState = IDLE;
- mSoundID = 0;
- mSound.reset();
- mAudioTrack.clear(); // actual release from garbage
+ // do not create a new audio track if current track is compatible with sound parameters
+
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.packageName = mStreamManager->getOpPackageName();
+ attributionSource.token = sp<BBinder>::make();
+ // TODO b/182469354 make consistent with AudioRecord, add util for native source
+ mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
+ channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+ staticCallback, userData,
+ 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
+ AudioTrack::TRANSFER_DEFAULT,
+ nullptr /*offloadInfo*/, attributionSource,
+ mStreamManager->getAttributes(),
+ false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
+ mAudioTrack->setCallerName("soundpool");
+
+ if (status_t status = mAudioTrack->initCheck();
+ status != NO_ERROR) {
+ ALOGE("%s: error %d creating AudioTrack", __func__, status);
+ // TODO: should we consider keeping the soundID and reusing the old track?
+ mState = IDLE;
+ mSoundID = 0;
+ mSound.reset();
+ garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack.
+ mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
+ return;
+ }
+ // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+ mToggle = toggle;
+ ALOGV("%s: using new track %p for sound %d",
+ __func__, mAudioTrack.get(), sound->getSoundID());
}
-
- // move tracks to garbage to be released later outside of lock.
- if (newTrack) garbage.emplace_back(std::move(newTrack));
- if (oldTrack) garbage.emplace_back(std::move(oldTrack));
+ if (mMuted) {
+ mAudioTrack->setVolume(0.f, 0.f);
+ } else {
+ mAudioTrack->setVolume(leftVolume, rightVolume);
+ }
+ mAudioTrack->setLoop(0, frameCount, loop);
+ mAudioTrack->start();
+ mSound = sound;
+ mSoundID = sound->getSoundID();
+ mPriority = priority;
+ mLoop = loop;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mRate = rate;
+ mState = PLAYING;
+ mStopTimeNs = 0;
+ mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
}
/* static */
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index a6c1b50..32fd734 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -18,6 +18,10 @@
android_getprocnetwork; # llndk
android_setprocdns; # llndk
android_getprocdns; # llndk
+ # These functions have been part of the NDK since API 33.
+ android_tag_socket_with_uid; # llndk
+ android_tag_socket; # llndk
+ android_untag_socket; # llndk
local:
*;
};
diff --git a/native/android/net.c b/native/android/net.c
index e2f36a7..d7c22e1 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -161,3 +161,15 @@
void android_res_cancel(int nsend_fd) {
resNetworkCancel(nsend_fd);
}
+
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) {
+ return tagSocket(sockfd, tag, uid);
+}
+
+int android_tag_socket(int sockfd, int tag) {
+ return tagSocket(sockfd, tag, -1);
+}
+
+int android_untag_socket(int sockfd) {
+ return untagSocket(sockfd);
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 8043a37..cc7b2a5 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -54,6 +54,7 @@
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Provides access to network usage history and statistics. Usage data is collected in
@@ -568,6 +569,31 @@
return result;
}
+ /**
+ * Query realtime network usage statistics details with interfaces constrains.
+ * Return snapshot of current UID statistics, including any {@link TrafficStats#UID_TETHERING},
+ * video calling data usage and count of network operations that set by
+ * {@link TrafficStats#incrementOperationCount}. The returned data doesn't include any
+ * statistics that is reported by {@link NetworkStatsProvider}.
+ *
+ * @param requiredIfaces A list of interfaces the stats should be restricted to, or
+ * {@link NetworkStats#INTERFACES_ALL}.
+ *
+ * @hide
+ */
+ //@SystemApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ @NonNull public android.net.NetworkStats getDetailedUidStats(
+ @NonNull Set<String> requiredIfaces) {
+ Objects.requireNonNull(requiredIfaces, "requiredIfaces cannot be null");
+ try {
+ return mService.getDetailedUidStats(requiredIfaces.toArray(new String[0]));
+ } catch (RemoteException e) {
+ if (DBG) Log.d(TAG, "Remote exception when get detailed uid stats");
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public void registerUsageCallback(NetworkTemplate template, int networkType,
long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 2b73eb6..3273e88 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -44,6 +44,7 @@
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
@@ -70,7 +71,7 @@
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
@@ -89,7 +90,6 @@
import android.database.ContentObserver;
import android.net.DataUsageRequest;
import android.net.INetd;
-import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.Network;
@@ -106,6 +106,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.TetherStatsParcel;
import android.net.TetheringManager;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
@@ -120,12 +121,12 @@
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
@@ -148,6 +149,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FileRotator;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BestClock;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.CollectionUtils;
@@ -208,7 +210,6 @@
private static final String TAG_NETSTATS_ERROR = "netstats_error";
private final Context mContext;
- private final INetworkManagementService mNetworkManager;
private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
private final Clock mClock;
@@ -223,6 +224,9 @@
private final ContentObserver mContentObserver;
private final ContentResolver mContentResolver;
+ protected INetd mNetd;
+ private final AlertObserver mAlertObserver = new AlertObserver();
+
@VisibleForTesting
public static final String ACTION_NETWORK_STATS_POLL =
"com.android.server.action.NETWORK_STATS_POLL";
@@ -405,15 +409,16 @@
}
}
- public static NetworkStatsService create(Context context,
- INetworkManagementService networkManager) {
+ /** Creates a new NetworkStatsService */
+ public static NetworkStatsService create(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock =
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
final INetd netd = INetd.Stub.asInterface(
(IBinder) context.getSystemService(Context.NETD_SERVICE));
- final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+ final NetworkStatsService service = new NetworkStatsService(context,
+ INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
alarmManager, wakeLock, getDefaultClock(),
new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
@@ -426,14 +431,12 @@
// This must not be called outside of tests, even within the same package, as this constructor
// does not register the local service. Use the create() helper above.
@VisibleForTesting
- NetworkStatsService(Context context, INetworkManagementService networkManager,
- AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
- NetworkStatsSettings settings, NetworkStatsFactory factory,
- NetworkStatsObservers statsObservers, File systemDir, File baseDir,
- @NonNull Dependencies deps) {
+ NetworkStatsService(Context context, INetd netd, AlarmManager alarmManager,
+ PowerManager.WakeLock wakeLock, Clock clock, NetworkStatsSettings settings,
+ NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
+ File baseDir, @NonNull Dependencies deps) {
mContext = Objects.requireNonNull(context, "missing Context");
- mNetworkManager = Objects.requireNonNull(networkManager,
- "missing INetworkManagementService");
+ mNetd = Objects.requireNonNull(netd, "missing Netd");
mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
mClock = Objects.requireNonNull(clock, "missing Clock");
mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
@@ -506,6 +509,26 @@
new NetworkStatsManagerInternalImpl());
}
+ /**
+ * Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
+ */
+ @VisibleForTesting
+ public class AlertObserver extends BaseNetdUnsolicitedEventListener {
+ @Override
+ public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+
+ if (LIMIT_GLOBAL_ALERT.equals(alertName)) {
+ // kick off background poll to collect network stats unless there is already
+ // such a call pending; UID stats are handled during normal polling interval.
+ if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+ mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+ mSettings.getPollDelay());
+ }
+ }
+ }
+ }
+
public void systemReady() {
synchronized (mStatsLock) {
mSystemReady = true;
@@ -551,9 +574,9 @@
mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
try {
- mNetworkManager.registerObserver(mAlertObserver);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
+ mNetd.registerUnsolicitedEventListener(mAlertObserver);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.wtf(TAG, "Error registering event listener :", e);
}
// schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
@@ -641,13 +664,13 @@
}
/**
- * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+ * Register for a global alert that is delivered through {@link AlertObserver}
* or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
* been transferred.
*/
private void registerGlobalAlert() {
try {
- mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
+ mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes);
} catch (IllegalStateException e) {
Log.w(TAG, "problem registering for global alert: " + e);
} catch (RemoteException e) {
@@ -932,6 +955,7 @@
@Override
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
try {
final String[] ifacesToQuery =
mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
@@ -1236,26 +1260,6 @@
};
/**
- * Observer that watches for {@link INetworkManagementService} alerts.
- */
- private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
- @Override
- public void limitReached(String limitName, String iface) {
- // only someone like NMS should be calling us
- PermissionUtils.enforceNetworkStackPermission(mContext);
-
- if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
- // kick off background poll to collect network stats unless there is already
- // such a call pending; UID stats are handled during normal polling interval.
- if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
- mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
- mSettings.getPollDelay());
- }
- }
- }
- };
-
- /**
* Handle collapsed RAT type changed event.
*/
@VisibleForTesting
@@ -1966,13 +1970,29 @@
*/
// TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
// tethering stats.
- private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+ private @NonNull NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+ // We only need to return per-UID stats. Per-device stats are already counted by
+ // interface counters.
+ if (how != STATS_PER_UID) {
+ return new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ }
+
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
try {
- return mNetworkManager.getNetworkStatsTethering(how);
+ final TetherStatsParcel[] tetherStatsParcels = mNetd.tetherGetStats();
+ for (TetherStatsParcel tetherStats : tetherStatsParcels) {
+ try {
+ stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING,
+ SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets,
+ tetherStats.txBytes, tetherStats.txPackets, 0L));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalStateException("invalid tethering stats " + e);
+ }
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
- return new NetworkStats(0L, 10);
}
+ return stats;
}
// TODO: It is copied from ConnectivityService, consider refactor these check permission
@@ -2054,7 +2074,7 @@
@NonNull final INetworkStatsProvider mProvider;
@NonNull private final Semaphore mSemaphore;
- @NonNull final INetworkManagementEventObserver mAlertObserver;
+ @NonNull final AlertObserver mAlertObserver;
@NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
@NonNull private final Object mProviderStatsLock = new Object();
@@ -2068,7 +2088,7 @@
NetworkStatsProviderCallbackImpl(
@NonNull String tag, @NonNull INetworkStatsProvider provider,
@NonNull Semaphore semaphore,
- @NonNull INetworkManagementEventObserver alertObserver,
+ @NonNull AlertObserver alertObserver,
@NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
throws RemoteException {
mTag = tag;
@@ -2116,7 +2136,7 @@
// This binder object can only have been obtained by a process that holds
// NETWORK_STATS_PROVIDER. Thus, no additional permission check is required.
BinderUtils.withCleanCallingIdentity(() ->
- mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
+ mAlertObserver.onQuotaLimitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
}
@Override
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 450e988..c8b4f11 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -46,6 +46,7 @@
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.IBluetoothLeCallControl;
import android.content.ActivityNotFoundException;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
@@ -1328,11 +1329,15 @@
+ bluetoothProfile);
}
- if (bluetoothProfile != BluetoothProfile.HEADSET) {
+ Intent intent;
+ if (bluetoothProfile == BluetoothProfile.HEADSET) {
+ intent = new Intent(IBluetoothHeadset.class.getName());
+ } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
+ intent = new Intent(IBluetoothLeCallControl.class.getName());
+ } else {
return false;
}
- Intent intent = new Intent(IBluetoothHeadset.class.getName());
psc = new ProfileServiceConnections(intent);
if (!psc.bindService()) {
return false;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a2c2dbd..3951680 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -39,6 +39,8 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.TrafficStats.UID_TETHERING;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
@@ -133,12 +135,6 @@
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
- /**
- * Name representing {@link #setGlobalAlert(long)} limit when delivered to
- * {@link INetworkManagementEventObserver#limitReached(String, String)}.
- */
- public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
-
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
static final boolean MODIFY_OPERATION_ADD = true;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 7ba032f..c6bca19 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -16,6 +16,7 @@
package com.android.server.am;
import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
@@ -702,8 +703,10 @@
if (wifiInfo.isValid()) {
final long wifiChargeUC = measuredEnergyDeltas != null ?
measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
- mStats.updateWifiState(
- extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
+ final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+ NetworkStatsManager.class);
+ mStats.updateWifiState(extractDeltaLocked(wifiInfo),
+ wifiChargeUC, elapsedRealtime, uptime, networkStatsManager);
} else {
Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
}
@@ -712,8 +715,10 @@
if (modemInfo != null) {
final long mobileRadioChargeUC = measuredEnergyDeltas != null
? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+ final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+ NetworkStatsManager.class);
mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
- uptime);
+ uptime, networkStatsManager);
}
if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9a349ef..d75ddba 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.app.StatsManager;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -2025,8 +2026,11 @@
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
+ final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+ NetworkStatsManager.class);
mHandler.post(() -> {
- mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
+ mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime,
+ networkStatsManager);
});
}
}
@@ -2063,9 +2067,11 @@
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
+ final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+ NetworkStatsManager.class);
mHandler.post(() -> {
mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
- uptime);
+ uptime, networkStatsManager);
});
}
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd5..3d5abfe 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -535,9 +535,6 @@
mEmulatorClipboardMonitor.accept(clip);
final int userId = UserHandle.getUserId(uid);
- if (clip != null) {
- startClassificationLocked(clip, userId);
- }
// Update this user
setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -593,6 +590,17 @@
@GuardedBy("mLock")
private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
int uid, @Nullable String sourcePackage) {
+ final int userId = UserHandle.getUserId(uid);
+ if (clip != null) {
+ startClassificationLocked(clip, userId);
+ }
+
+ setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
+ }
+
+ @GuardedBy("mLock")
+ private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+ @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
revokeUris(clipboard);
clipboard.activePermissionOwners.clear();
if (clip == null && clipboard.primaryClip == null) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2c666b5..9a9c3ea 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1208,8 +1208,11 @@
for (RouteInfo route : mConfig.routes) {
lp.addRoute(route);
InetAddress address = route.getDestination().getAddress();
- allowIPv4 |= address instanceof Inet4Address;
- allowIPv6 |= address instanceof Inet6Address;
+
+ if (route.getType() == RouteInfo.RTN_UNICAST) {
+ allowIPv4 |= address instanceof Inet4Address;
+ allowIPv6 |= address instanceof Inet6Address;
+ }
}
}
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 2e2d812..8097f4e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -1,8 +1,6 @@
+# Bug component: 137631
elaurent@google.com
-hdmoon@google.com
-insun@google.com
-jaewan@google.com
-jinpark@google.com
-klhyun@google.com
lajos@google.com
-sungsoo@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2ca057d..60bd4c1 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -130,7 +130,7 @@
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index ef0079e..ca67597 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -35,7 +35,6 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -313,12 +312,12 @@
return mStartWallTime;
}
- public PowerStatsLogger(Context context, File dataStoragePath,
+ public PowerStatsLogger(Context context, Looper looper, File dataStoragePath,
String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- super(Looper.getMainLooper());
+ super(looper);
mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
mPowerStatsHALWrapper = powerStatsHALWrapper;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index bb52c1d..9953ca8 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -28,6 +28,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.UserHandle;
import android.power.PowerStatsInternal;
import android.util.Slog;
@@ -79,6 +80,9 @@
private StatsPullAtomCallbackImpl mPullAtomCallback;
@Nullable
private PowerStatsInternal mPowerStatsInternal;
+ @Nullable
+ @GuardedBy("this")
+ private Looper mLooper;
@VisibleForTesting
static class Injector {
@@ -127,12 +131,12 @@
}
}
- PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String meterCacheFilename,
+ PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+ File dataStoragePath, String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new PowerStatsLogger(context, dataStoragePath,
+ return new PowerStatsLogger(context, looper, dataStoragePath,
meterFilename, meterCacheFilename,
modelFilename, modelCacheFilename,
residencyFilename, residencyCacheFilename,
@@ -229,11 +233,11 @@
mDataStoragePath = mInjector.createDataStoragePath();
// Only start logger and triggers if initialization is successful.
- mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath,
- mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(),
- mInjector.createModelFilename(), mInjector.createModelCacheFilename(),
- mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(),
- getPowerStatsHal());
+ mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(),
+ mDataStoragePath, mInjector.createMeterFilename(),
+ mInjector.createMeterCacheFilename(), mInjector.createModelFilename(),
+ mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(),
+ mInjector.createResidencyCacheFilename(), getPowerStatsHal());
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
@@ -245,6 +249,17 @@
return mInjector.getPowerStatsHALWrapperImpl();
}
+ private Looper getLooper() {
+ synchronized (this) {
+ if (mLooper == null) {
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ return thread.getLooper();
+ }
+ return mLooper;
+ }
+ }
+
public PowerStatsService(Context context) {
this(context, new Injector());
}
@@ -260,9 +275,7 @@
private final Handler mHandler;
LocalService() {
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- mHandler = new Handler(thread.getLooper());
+ mHandler = new Handler(getLooper());
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 0f37450..e7d05b6 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -527,12 +527,13 @@
callback.onFailure();
return;
}
- textClassifierServiceConsumer.accept(serviceState.mService);
+ consumeServiceNoExceptLocked(textClassifierServiceConsumer, serviceState.mService);
} else {
serviceState.mPendingRequests.add(
new PendingRequest(
methodName,
- () -> textClassifierServiceConsumer.accept(serviceState.mService),
+ () -> consumeServiceNoExceptLocked(
+ textClassifierServiceConsumer, serviceState.mService),
callback::onFailure, callback.asBinder(),
this,
serviceState,
@@ -541,6 +542,16 @@
}
}
+ private static void consumeServiceNoExceptLocked(
+ @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
+ @Nullable ITextClassifierService service) {
+ try {
+ textClassifierServiceConsumer.accept(service);
+ } catch (RuntimeException | Error e) {
+ Slog.e(LOG_TAG, "Exception when consume textClassifierService: " + e);
+ }
+ }
+
private static ITextClassifierCallback wrap(ITextClassifierCallback orig) {
return new CallbackWrapper(orig);
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index e0cc8e1..f29c40f 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -39,10 +39,13 @@
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -57,6 +60,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -148,6 +152,10 @@
@NonNull private final VcnContentResolver mContentResolver;
@NonNull private final ContentObserver mMobileDataSettingsObserver;
+ @NonNull
+ private final Map<Integer, VcnUserMobileDataStateListener> mMobileDataStateListeners =
+ new ArrayMap<>();
+
/**
* Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
*
@@ -221,6 +229,9 @@
// Update mIsMobileDataEnabled before starting handling of NetworkRequests.
mIsMobileDataEnabled = getMobileDataStatus();
+ // Register mobile data state listeners.
+ updateMobileDataStateListeners();
+
// Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
@@ -348,6 +359,12 @@
gatewayConnection.teardownAsynchronously();
}
+ // Unregister MobileDataStateListeners
+ for (VcnUserMobileDataStateListener listener : mMobileDataStateListeners.values()) {
+ getTelephonyManager().unregisterTelephonyCallback(listener);
+ }
+ mMobileDataStateListeners.clear();
+
mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
}
@@ -454,11 +471,40 @@
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
+ updateMobileDataStateListeners();
+
// Update the mobile data state after updating the subscription snapshot as a change in
// subIds for a subGroup may affect the mobile data state.
handleMobileDataToggled();
}
+ private void updateMobileDataStateListeners() {
+ final Set<Integer> subIdsInGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+ final HandlerExecutor executor = new HandlerExecutor(this);
+
+ // Register new callbacks
+ for (int subId : subIdsInGroup) {
+ if (!mMobileDataStateListeners.containsKey(subId)) {
+ final VcnUserMobileDataStateListener listener =
+ new VcnUserMobileDataStateListener();
+
+ getTelephonyManagerForSubid(subId).registerTelephonyCallback(executor, listener);
+ mMobileDataStateListeners.put(subId, listener);
+ }
+ }
+
+ // Unregister old callbacks
+ Iterator<Entry<Integer, VcnUserMobileDataStateListener>> iterator =
+ mMobileDataStateListeners.entrySet().iterator();
+ while (iterator.hasNext()) {
+ final Entry<Integer, VcnUserMobileDataStateListener> entry = iterator.next();
+ if (!subIdsInGroup.contains(entry.getKey())) {
+ getTelephonyManager().unregisterTelephonyCallback(entry.getValue());
+ iterator.remove();
+ }
+ }
+ }
+
private void handleMobileDataToggled() {
final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
mIsMobileDataEnabled = getMobileDataStatus();
@@ -493,11 +539,8 @@
}
private boolean getMobileDataStatus() {
- final TelephonyManager genericTelMan =
- mVcnContext.getContext().getSystemService(TelephonyManager.class);
-
for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
- if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
+ if (getTelephonyManagerForSubid(subId).isDataEnabled()) {
return true;
}
}
@@ -517,6 +560,14 @@
return request.canBeSatisfiedBy(builder.build());
}
+ private TelephonyManager getTelephonyManager() {
+ return mVcnContext.getContext().getSystemService(TelephonyManager.class);
+ }
+
+ private TelephonyManager getTelephonyManagerForSubid(int subid) {
+ return getTelephonyManager().createForSubscriptionId(subid);
+ }
+
private String getLogPrefix() {
return "["
+ LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
@@ -670,6 +721,16 @@
}
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ class VcnUserMobileDataStateListener extends TelephonyCallback
+ implements TelephonyCallback.UserMobileDataStateListener {
+
+ @Override
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
+ }
+ }
+
/** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
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 bd8d13b..6db25b7 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -20,6 +20,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
@@ -44,7 +46,7 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Set;
/** @hide */
@@ -76,7 +78,7 @@
public static int calculatePriorityClass(
VcnContext vcnContext,
UnderlyingNetworkRecord networkRecord,
- LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -94,7 +96,7 @@
}
int priorityIndex = 0;
- for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) {
+ for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) {
if (checkMatchesPriorityRule(
vcnContext,
nwPriority,
@@ -122,7 +124,11 @@
// TODO: Check Network Quality reported by metric monitors/probers.
final NetworkCapabilities caps = networkRecord.networkCapabilities;
- if (!networkPriority.allowMetered() && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+
+ final int meteredMatch = networkPriority.getMetered();
+ final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ if (meteredMatch == MATCH_REQUIRED && !isMetered
+ || meteredMatch == MATCH_FORBIDDEN && isMetered) {
return false;
}
@@ -171,7 +177,8 @@
return false;
}
- if (networkPriority.getSsid() != null && networkPriority.getSsid() != caps.getSsid()) {
+ if (!networkPriority.getSsids().isEmpty()
+ && !networkPriority.getSsids().contains(caps.getSsid())) {
return false;
}
@@ -226,26 +233,31 @@
.getSystemService(TelephonyManager.class)
.createForSubscriptionId(subId);
- if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) {
+ if (!networkPriority.getOperatorPlmnIds().isEmpty()) {
final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
- if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) {
+ if (!networkPriority.getOperatorPlmnIds().contains(plmnId)) {
return false;
}
}
- if (!networkPriority.getAllowedSpecificCarrierIds().isEmpty()) {
+ if (!networkPriority.getSimSpecificCarrierIds().isEmpty()) {
final int carrierId = subIdSpecificTelephonyMgr.getSimSpecificCarrierId();
- if (!networkPriority.getAllowedSpecificCarrierIds().contains(carrierId)) {
+ if (!networkPriority.getSimSpecificCarrierIds().contains(carrierId)) {
return false;
}
}
- if (!networkPriority.allowRoaming() && !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+ final int roamingMatch = networkPriority.getRoaming();
+ final boolean isRoaming = !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (roamingMatch == MATCH_REQUIRED && !isRoaming
+ || roamingMatch == MATCH_FORBIDDEN && isRoaming) {
return false;
}
- if (networkPriority.requireOpportunistic()) {
- if (!isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+ final int opportunisticMatch = networkPriority.getOpportunistic();
+ final boolean isOpportunistic = isOpportunistic(snapshot, caps.getSubscriptionIds());
+ if (opportunisticMatch == MATCH_REQUIRED) {
+ if (!isOpportunistic) {
return false;
}
@@ -265,6 +277,8 @@
.contains(SubscriptionManager.getActiveDataSubscriptionId())) {
return false;
}
+ } else if (opportunisticMatch == MATCH_FORBIDDEN && !isOpportunistic) {
+ return false;
}
return true;
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 df2f0d5..c0488b1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -32,7 +32,7 @@
import com.android.server.vcn.VcnContext;
import java.util.Comparator;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Objects;
/**
@@ -77,7 +77,7 @@
static Comparator<UnderlyingNetworkRecord> getComparator(
VcnContext vcnContext,
- LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -87,7 +87,7 @@
NetworkPriorityClassifier.calculatePriorityClass(
vcnContext,
left,
- underlyingNetworkPriorities,
+ underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
@@ -96,7 +96,7 @@
NetworkPriorityClassifier.calculatePriorityClass(
vcnContext,
right,
- underlyingNetworkPriorities,
+ underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
@@ -133,7 +133,7 @@
void dump(
VcnContext vcnContext,
IndentingPrintWriter pw,
- LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -145,7 +145,7 @@
NetworkPriorityClassifier.calculatePriorityClass(
vcnContext,
this,
- underlyingNetworkPriorities,
+ underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2aee778..2303495 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1837,7 +1837,7 @@
t.traceBegin("StartNetworkStatsService");
try {
- networkStats = NetworkStatsService.create(context, networkManagement);
+ networkStats = NetworkStatsService.create(context);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/services/midi/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 26b34fd..304fe5a 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -30,6 +30,7 @@
import android.hardware.power.stats.State;
import android.hardware.power.stats.StateResidency;
import android.hardware.power.stats.StateResidencyResult;
+import android.os.Looper;
import androidx.test.InstrumentationRegistry;
@@ -145,12 +146,12 @@
}
@Override
- PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String meterCacheFilename,
+ PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+ File dataStoragePath, String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath,
+ mPowerStatsLogger = new PowerStatsLogger(context, looper, dataStoragePath,
meterFilename, meterCacheFilename,
modelFilename, modelCacheFilename,
residencyFilename, residencyCacheFilename,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b109c46..de97730 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4724,7 +4724,7 @@
/**
* A priority list of ePDG addresses to be used. Possible values are {@link
* #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, {@link #EPDG_ADDRESS_PCO}, {@link
- * #EPDG_ADDRESS_CELLULAR_LOC}
+ * #EPDG_ADDRESS_CELLULAR_LOC}, {@link #EPDG_ADDRESS_VISITED_COUNTRY}
*/
public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY =
KEY_PREFIX + "epdg_address_priority_int_array";
@@ -4899,7 +4899,8 @@
EPDG_ADDRESS_STATIC,
EPDG_ADDRESS_PLMN,
EPDG_ADDRESS_PCO,
- EPDG_ADDRESS_CELLULAR_LOC
+ EPDG_ADDRESS_CELLULAR_LOC,
+ EPDG_ADDRESS_VISITED_COUNTRY
})
public @interface EpdgAddressType {}
@@ -4913,6 +4914,8 @@
public static final int EPDG_ADDRESS_PCO = 2;
/** Use cellular location to chose epdg server */
public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
+ /* Use Visited Country FQDN rule*/
+ public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4;
/** @hide */
@IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index d03aee2..4a724b7 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -15,12 +15,13 @@
*/
package android.net.vcn;
+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.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import org.junit.Test;
@@ -32,26 +33,26 @@
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnCellUnderlyingNetworkTemplate getTestNetworkPriority() {
+ static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
- .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
- .setAllowRoaming(true /* allowRoaming */)
- .setRequireOpportunistic(true /* requireOpportunistic */)
+ .setMetered(MATCH_FORBIDDEN)
+ .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
+ .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+ .setRoaming(MATCH_FORBIDDEN)
+ .setOpportunistic(MATCH_REQUIRED)
.build();
}
@Test
public void testBuilderAndGetters() {
- final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
- assertTrue(networkPriority.allowMetered());
- assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
- assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
- assertTrue(networkPriority.allowRoaming());
- assertTrue(networkPriority.requireOpportunistic());
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
+ assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
+ assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
}
@Test
@@ -59,16 +60,16 @@
final VcnCellUnderlyingNetworkTemplate networkPriority =
new VcnCellUnderlyingNetworkTemplate.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
- assertFalse(networkPriority.allowMetered());
- assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
- assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
- assertFalse(networkPriority.allowRoaming());
- assertFalse(networkPriority.requireOpportunistic());
+ assertEquals(MATCH_ANY, networkPriority.getMetered());
+ assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
+ assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
+ assertEquals(MATCH_ANY, networkPriority.getRoaming());
+ assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
}
@Test
public void testPersistableBundle() {
- final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
networkPriority,
VcnUnderlyingNetworkTemplate.fromPersistableBundle(
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 1f2905d..2aef9ae 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,8 +17,8 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES;
-import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
+import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -40,8 +40,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -54,17 +55,17 @@
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
- private static final LinkedHashSet<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_PRIORITIES =
- new LinkedHashSet();
+ private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES =
+ new ArrayList();
static {
Arrays.sort(EXPOSED_CAPS);
Arrays.sort(UNDERLYING_CAPS);
- UNDERLYING_NETWORK_PRIORITIES.add(
- VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
- UNDERLYING_NETWORK_PRIORITIES.add(
- VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ UNDERLYING_NETWORK_TEMPLATES.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ UNDERLYING_NETWORK_TEMPLATES.add(
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
}
public static final long[] RETRY_INTERVALS_MS =
@@ -95,7 +96,7 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+ newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
@@ -174,10 +175,10 @@
}
@Test
- public void testBuilderRequiresNonNullNetworkPriorities() {
+ public void testBuilderRequiresNonNullNetworkTemplates() {
try {
newBuilder().setVcnUnderlyingNetworkPriorities(null);
- fail("Expected exception due to invalid underlyingNetworkPriorities");
+ fail("Expected exception due to invalid underlyingNetworkTemplates");
} catch (NullPointerException e) {
}
}
@@ -219,7 +220,7 @@
Arrays.sort(exposedCaps);
assertArrayEquals(EXPOSED_CAPS, exposedCaps);
- assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+ assertEquals(UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
@@ -234,13 +235,13 @@
}
@Test
- public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() {
+ public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
- configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null);
+ configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
assertEquals(
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
}
private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
@@ -285,39 +286,36 @@
assertNotEquals(config, anotherConfig);
}
- private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
- LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPriorities) {
+ private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkTemplates(
+ List<VcnUnderlyingNetworkTemplate> networkTemplates) {
return buildTestConfigWithExposedCaps(
new VcnGatewayConnectionConfig.Builder(
- "buildTestConfigWithVcnUnderlyingNetworkPriorities",
+ "buildTestConfigWithVcnUnderlyingNetworkTemplates",
TUNNEL_CONNECTION_PARAMS)
- .setVcnUnderlyingNetworkPriorities(networkPriorities),
+ .setVcnUnderlyingNetworkPriorities(networkTemplates),
EXPOSED_CAPS);
}
@Test
- public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception {
+ public void testVcnUnderlyingNetworkTemplatesEquality() throws Exception {
final VcnGatewayConnectionConfig config =
- buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(UNDERLYING_NETWORK_TEMPLATES);
- final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesEqual =
- new LinkedHashSet();
- networkPrioritiesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
- networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ final List<VcnUnderlyingNetworkTemplate> networkTemplatesEqual = new ArrayList();
+ networkTemplatesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ networkTemplatesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
final VcnGatewayConnectionConfig configEqual =
- buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesEqual);
- final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesNotEqual =
- new LinkedHashSet();
- networkPrioritiesNotEqual.add(
- VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ final List<VcnUnderlyingNetworkTemplate> networkTemplatesNotEqual = new ArrayList();
+ networkTemplatesNotEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
final VcnGatewayConnectionConfig configNotEqual =
- buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesNotEqual);
- assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual);
+ assertEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesEqual);
assertEquals(config, configEqual);
- assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual);
+ assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
assertNotEquals(config, configNotEqual);
}
}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index 652057f..cb5b47b 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -15,36 +15,38 @@
*/
package android.net.vcn;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
+import java.util.Set;
+
public class VcnWifiUnderlyingNetworkTemplateTest {
private static final String SSID = "TestWifi";
private static final int INVALID_NETWORK_QUALITY = -1;
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnWifiUnderlyingNetworkTemplate getTestNetworkPriority() {
+ static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
return new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setSsid(SSID)
+ .setMetered(MATCH_FORBIDDEN)
+ .setSsids(Set.of(SSID))
.build();
}
@Test
public void testBuilderAndGetters() {
- final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
- assertTrue(networkPriority.allowMetered());
- assertEquals(SSID, networkPriority.getSsid());
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(Set.of(SSID), networkPriority.getSsids());
}
@Test
@@ -52,8 +54,8 @@
final VcnWifiUnderlyingNetworkTemplate networkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
- assertFalse(networkPriority.allowMetered());
- assertNull(SSID, networkPriority.getSsid());
+ assertEquals(MATCH_ANY, networkPriority.getMetered());
+ assertTrue(networkPriority.getSsids().isEmpty());
}
@Test
@@ -68,7 +70,7 @@
@Test
public void testPersistableBundle() {
- final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
networkPriority,
VcnUnderlyingNetworkTemplate.fromPersistableBundle(
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 5d2f9d7..6d26968 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -58,6 +58,7 @@
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
@@ -208,6 +209,13 @@
}
@Test
+ public void testMobileDataStateListenersRegistered() {
+ // Validate state from setUp()
+ verify(mTelephonyManager, times(3))
+ .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
+ }
+
+ @Test
public void testMobileDataStateCheckedOnInitialization_enabled() {
// Validate state from setUp()
assertTrue(mVcn.isMobileDataEnabled());
@@ -263,6 +271,24 @@
assertFalse(mVcn.isMobileDataEnabled());
}
+ @Test
+ public void testSubscriptionSnapshotUpdatesMobileDataStateListeners() {
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+
+ doReturn(new ArraySet<>(Arrays.asList(2, 4)))
+ .when(updatedSnapshot)
+ .getAllSubIdsInGroup(any());
+
+ mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+ mTestLooper.dispatchAll();
+
+ verify(mTelephonyManager, times(4))
+ .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
+ verify(mTelephonyManager, times(2))
+ .unregisterTelephonyCallback(any(VcnUserMobileDataStateListener.class));
+ }
+
private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
for (final int[] caps : TEST_CAPS) {
startVcnGatewayWithCapabilities(requestListener, caps);
@@ -402,24 +428,17 @@
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
}
- private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) {
- final ArgumentCaptor<ContentObserver> captor =
- ArgumentCaptor.forClass(ContentObserver.class);
- verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
- final ContentObserver contentObserver = captor.getValue();
-
+ private void setupForMobileDataTest(boolean startingToggleState) {
// Start VcnGatewayConnections
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
mVcn.setMobileDataEnabled(startingToggleState);
triggerVcnRequestListeners(requestListener);
- final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
- mVcn.getVcnGatewayConnectionConfigMap();
+ }
- // Trigger data toggle change.
- doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
- contentObserver.onChange(false /* selfChange, ignored */);
- mTestLooper.dispatchAll();
-
+ private void verifyMobileDataToggledUpdatesGatewayConnections(
+ boolean startingToggleState,
+ boolean endingToggleState,
+ Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways) {
// Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
// toggle state changed.
for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
@@ -433,29 +452,98 @@
}
}
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
if (startingToggleState != endingToggleState) {
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
}
assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
}
- @Test
- public void testMobileDataEnabled() {
- verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
+ private void verifyGlobalMobileDataToggled(
+ boolean startingToggleState, boolean endingToggleState) {
+ setupForMobileDataTest(startingToggleState);
+ final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+ mVcn.getVcnGatewayConnectionConfigMap();
+
+ // Trigger data toggle change
+ final ArgumentCaptor<ContentObserver> captor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
+ final ContentObserver contentObserver = captor.getValue();
+
+ doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+ contentObserver.onChange(false /* selfChange, ignored */);
+ mTestLooper.dispatchAll();
+
+ // Verify resultant behavior
+ verifyMobileDataToggledUpdatesGatewayConnections(
+ startingToggleState, endingToggleState, gateways);
}
@Test
- public void testMobileDataDisabled() {
- verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */);
+ public void testGlobalMobileDataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, true /* endingToggleState */);
}
@Test
- public void testMobileDataObserverFiredWithoutChanges_dataEnabled() {
- verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */);
+ public void testGlobalMobileDataDisabled() {
+ verifyGlobalMobileDataToggled(
+ true /* startingToggleState */, false /* endingToggleState */);
}
@Test
- public void testMobileDataObserverFiredWithoutChanges_dataDisabled() {
- verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+ public void testGlobalMobileDataObserverFiredWithoutChanges_dataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testGlobalMobileDataObserverFiredWithoutChanges_dataDisabled() {
+ verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+ }
+
+ private void verifySubscriptionMobileDataToggled(
+ boolean startingToggleState, boolean endingToggleState) {
+ setupForMobileDataTest(startingToggleState);
+ final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+ mVcn.getVcnGatewayConnectionConfigMap();
+
+ // Trigger data toggle change.
+ final ArgumentCaptor<VcnUserMobileDataStateListener> captor =
+ ArgumentCaptor.forClass(VcnUserMobileDataStateListener.class);
+ verify(mTelephonyManager, times(3)).registerTelephonyCallback(any(), captor.capture());
+ final VcnUserMobileDataStateListener listener = captor.getValue();
+
+ doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+ listener.onUserMobileDataStateChanged(false /* enabled, ignored */);
+ mTestLooper.dispatchAll();
+
+ // Verify resultant behavior
+ verifyMobileDataToggledUpdatesGatewayConnections(
+ startingToggleState, endingToggleState, gateways);
+ }
+
+ @Test
+ public void testSubscriptionMobileDataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, true /* endingToggleState */);
+ }
+
+ @Test
+ public void testSubscriptionMobileDataDisabled() {
+ verifyGlobalMobileDataToggled(
+ true /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataDisabled() {
+ verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
}
}
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 f23d5bf..4bb7de8 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,8 @@
package com.android.server.vcn.routeselection;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
@@ -145,7 +147,7 @@
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(false /* allowMetered */)
+ .setMetered(MATCH_FORBIDDEN)
.build();
assertFalse(
@@ -164,7 +166,6 @@
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
.build();
final UnderlyingNetworkRecord selectedNetworkRecord =
isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -214,8 +215,7 @@
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setSsid(nwPrioritySsid)
+ .setSsids(Set.of(nwPrioritySsid))
.build();
assertEquals(
@@ -238,10 +238,7 @@
}
private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
- return new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowRoaming(true /* allowRoaming */);
+ return new VcnCellUnderlyingNetworkTemplate.Builder().setNetworkQuality(NETWORK_QUALITY_OK);
}
@Test
@@ -258,9 +255,7 @@
@Test
public void testMatchOpportunisticCell() {
final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
- getCellNetworkPriorityBuilder()
- .setRequireOpportunistic(true /* requireOpportunistic */)
- .build();
+ getCellNetworkPriorityBuilder().setOpportunistic(MATCH_REQUIRED).build();
when(mSubscriptionSnapshot.isOpportunistic(SUB_ID)).thenReturn(true);
when(mSubscriptionSnapshot.getAllSubIdsInGroup(SUB_GROUP)).thenReturn(new ArraySet<>());
@@ -279,7 +274,7 @@
final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
- .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
+ .setOperatorPlmnIds(Set.of(networkPriorityPlmnId))
.build();
assertEquals(
@@ -308,7 +303,7 @@
final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
- .setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId))
+ .setSimSpecificCarrierIds(Set.of(networkPriorityCarrierId))
.build();
assertEquals(
@@ -336,7 +331,7 @@
@Test
public void testMatchWifiFailWithoutNotRoamingBit() {
final VcnCellUnderlyingNetworkTemplate networkPriority =
- getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build();
+ getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build();
assertFalse(
checkMatchesCellPriorityRule(
@@ -353,7 +348,7 @@
calculatePriorityClass(
mVcnContext,
networkRecord,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
null /* currentlySelected */,
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 8975713..fbfbf68 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -19,12 +19,11 @@
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "android-base/utf8.h"
-
+#include "format/proto/ProtoDeserialize.h"
#include "io/StringStream.h"
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
-#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -59,55 +58,56 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
"integration-tests", "CompileTest", "res"});
+ const std::string kOutDir = testing::TempDir();
// Resource files without periods in the file name should not throw errors
const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
- const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+ const std::string path0_out = BuildPath({kOutDir, "values_values.arsc.flat"});
::android::base::utf8::unlink(path0_out.c_str());
- ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
- const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+ const std::string path1_out = BuildPath({kOutDir, "drawable_image.png.flat"});
::android::base::utf8::unlink(path1_out.c_str());
- ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
- const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+ const std::string path2_out = BuildPath({kOutDir, "drawable_image.9.png.flat"});
::android::base::utf8::unlink(path2_out.c_str());
- ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
// Resource files with periods in the file name should fail on non-legacy compilations
const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
- const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+ const std::string path3_out = BuildPath({kOutDir, "values_values.all.arsc.flat"});
::android::base::utf8::unlink(path3_out.c_str());
- ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path3, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path3, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
- const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+ const std::string path4_out = BuildPath({kOutDir, "drawable_image.small.png.flat"});
::android::base::utf8::unlink(path4_out.c_str());
- ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path4, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path4, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
- const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+ const std::string path5_out = BuildPath({kOutDir, "drawable_image.small.9.png.flat"});
::android::base::utf8::unlink(path5_out.c_str());
- ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path5, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path5, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
}
@@ -116,9 +116,7 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
"integration-tests", "CompileTest", "DirInput", "res"});
- const std::string kOutputFlata =
- BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
- "CompileTest", "DirInput", "compiled.flata"});
+ const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
@@ -147,9 +145,7 @@
const std::string kResZip =
BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
"CompileTest", "ZipInput", "res.zip"});
- const std::string kOutputFlata =
- BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
- "CompileTest", "ZipInput", "compiled.flata"});
+ const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
::android::base::utf8::unlink(kOutputFlata.c_str());
@@ -257,9 +253,9 @@
TEST_F(CompilerTest, RelativePathTest) {
StdErrDiagnostics diag;
- const std::string res_path = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "res"});
+ const std::string res_path =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "res"});
const std::string path_values_colors = GetTestPath("values/colors.xml");
WriteFile(path_values_colors, "<resources>"
@@ -272,9 +268,8 @@
"<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
"</LinearLayout>");
- const std::string compiled_files_dir = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "compiled"});
+ const std::string compiled_files_dir =
+ BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "compiled"});
CHECK(file::mkdirs(compiled_files_dir.data()));
const std::string path_values_colors_out =
@@ -283,9 +278,8 @@
BuildPath({compiled_files_dir, "layout_layout_one.flat"});
::android::base::utf8::unlink(path_values_colors_out.c_str());
::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
- const std::string apk_path = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "out.apk"});
+ const std::string apk_path =
+ BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "out.apk"});
const std::string source_set_res = BuildPath({"main", "res"});
const std::string relative_path_values_colors =
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 285e5a1..5a1b184 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -67,8 +67,7 @@
}
void TestDirectoryFixture::SetUp() {
- temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
- "_temp",
+ temp_dir_ = file::BuildPath({testing::TempDir(), "_temp",
testing::UnitTest::GetInstance()->current_test_case()->name(),
testing::UnitTest::GetInstance()->current_test_info()->name()});
ASSERT_TRUE(file::mkdirs(temp_dir_));
@@ -236,4 +235,4 @@
return args_;
}
-} // namespace aapt
\ No newline at end of file
+} // namespace aapt