Merge "Add an Ethernet tethering test."
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 2fbba68..00d0d9c 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -60,6 +60,7 @@
hostdex: true, // for hiddenapi check
visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
+ permitted_packages: ["android.net"],
}
stubs_defaults {
diff --git a/Tethering/common/TetheringLib/api/current.txt b/Tethering/common/TetheringLib/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Tethering/common/TetheringLib/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
new file mode 100644
index 0000000..754584e
--- /dev/null
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -0,0 +1,129 @@
+// Signature format: 2.0
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public final class TetheringConstants {
+ field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+ field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ }
+
+ public class TetheringManager {
+ ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+ method public int getLastTetherError(@NonNull String);
+ method @NonNull public String[] getTetherableBluetoothRegexs();
+ method @NonNull public String[] getTetherableIfaces();
+ method @NonNull public String[] getTetherableUsbRegexs();
+ method @NonNull public String[] getTetherableWifiRegexs();
+ method @NonNull public String[] getTetheredIfaces();
+ method @NonNull public String[] getTetheringErroredIfaces();
+ method public boolean isTetheringSupported();
+ method public boolean isTetheringSupported(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+ method @Deprecated public int setUsbTethering(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @Deprecated public int tether(@NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @Deprecated public int untether(@NonNull String);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
+ field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
+ field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public static interface TetheringManager.StartTetheringCallback {
+ method public default void onTetheringFailed(int);
+ method public default void onTetheringStarted();
+ }
+
+ public static interface TetheringManager.TetheringEventCallback {
+ method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public default void onError(@NonNull String, int);
+ method public default void onOffloadStatusChanged(int);
+ method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onTetheringSupported(boolean);
+ method public default void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ public static class TetheringManager.TetheringInterfaceRegexps {
+ method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+ method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+ method public boolean getShouldShowEntitlementUi();
+ method public int getTetheringType();
+ method public boolean isExemptFromEntitlementCheck();
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
+ }
+
+}
+
diff --git a/Tethering/common/TetheringLib/api/module-lib-removed.txt b/Tethering/common/TetheringLib/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Tethering/common/TetheringLib/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Tethering/common/TetheringLib/api/removed.txt b/Tethering/common/TetheringLib/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Tethering/common/TetheringLib/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
new file mode 100644
index 0000000..edd1ebb
--- /dev/null
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -0,0 +1,99 @@
+// Signature format: 2.0
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public class TetheringManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
+ field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
+ field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public static interface TetheringManager.StartTetheringCallback {
+ method public default void onTetheringFailed(int);
+ method public default void onTetheringStarted();
+ }
+
+ public static interface TetheringManager.TetheringEventCallback {
+ method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public default void onError(@NonNull String, int);
+ method public default void onOffloadStatusChanged(int);
+ method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public default void onTetheringSupported(boolean);
+ method public default void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+ method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+ method public boolean getShouldShowEntitlementUi();
+ method public int getTetheringType();
+ method public boolean isExemptFromEntitlementCheck();
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
+ }
+
+}
+
diff --git a/Tethering/common/TetheringLib/api/system-removed.txt b/Tethering/common/TetheringLib/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Tethering/common/TetheringLib/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index f8f89e7..096f4fe 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -115,6 +115,19 @@
*/
public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ TETHERING_WIFI,
+ TETHERING_USB,
+ TETHERING_BLUETOOTH,
+ TETHERING_WIFI_P2P,
+ TETHERING_NCM,
+ TETHERING_ETHERNET,
+ })
+ public @interface TetheringType {
+ }
+
/**
* Invalid tethering type.
* @see #startTethering.
@@ -158,22 +171,60 @@
*/
public static final int TETHERING_ETHERNET = 5;
- public static final int TETHER_ERROR_NO_ERROR = 0;
- public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
- public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
- public static final int TETHER_ERROR_UNSUPPORTED = 3;
- public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
- public static final int TETHER_ERROR_MASTER_ERROR = 5;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ TETHER_ERROR_NO_ERROR,
+ TETHER_ERROR_PROVISIONING_FAILED,
+ TETHER_ERROR_ENTITLEMENT_UNKNOWN,
+ })
+ public @interface EntitlementResult {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ TETHER_ERROR_NO_ERROR,
+ TETHER_ERROR_UNKNOWN_IFACE,
+ TETHER_ERROR_SERVICE_UNAVAIL,
+ TETHER_ERROR_INTERNAL_ERROR,
+ TETHER_ERROR_TETHER_IFACE_ERROR,
+ TETHER_ERROR_ENABLE_FORWARDING_ERROR,
+ TETHER_ERROR_DISABLE_FORWARDING_ERROR,
+ TETHER_ERROR_IFACE_CFG_ERROR,
+ TETHER_ERROR_DHCPSERVER_ERROR,
+ })
+ public @interface TetheringIfaceError {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ TETHER_ERROR_SERVICE_UNAVAIL,
+ TETHER_ERROR_INTERNAL_ERROR,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION,
+ TETHER_ERROR_UNKNOWN_TYPE,
+ })
+ public @interface StartTetheringError {
+ }
+
+ public static final int TETHER_ERROR_NO_ERROR = 0;
+ public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
+ public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
+ public static final int TETHER_ERROR_UNSUPPORTED = 3;
+ public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
+ public static final int TETHER_ERROR_INTERNAL_ERROR = 5;
public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
- public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8;
- public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
- public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
- public static final int TETHER_ERROR_PROVISION_FAILED = 11;
- public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
+ public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8;
+ public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9;
+ public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+ public static final int TETHER_ERROR_PROVISIONING_FAILED = 11;
+ public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13;
public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14;
public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15;
+ public static final int TETHER_ERROR_UNKNOWN_TYPE = 16;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -508,7 +559,7 @@
private final TetheringRequestParcel mBuilderParcel;
/** Default constructor of Builder. */
- public Builder(final int type) {
+ public Builder(@TetheringType final int type) {
mBuilderParcel = new TetheringRequestParcel();
mBuilderParcel.tetheringType = type;
mBuilderParcel.localIPv4Address = null;
@@ -553,11 +604,14 @@
return this;
}
- /** Start tethering without showing the provisioning UI. */
+ /**
+ * If an entitlement check is needed, sets whether to show the entitlement UI or to
+ * perform a silent entitlement check. By default, the entitlement UI is shown.
+ */
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
@NonNull
- public Builder setSilentProvisioning(boolean silent) {
- mBuilderParcel.showProvisioningUi = silent;
+ public Builder setShouldShowEntitlementUi(boolean showUi) {
+ mBuilderParcel.showProvisioningUi = showUi;
return this;
}
@@ -566,18 +620,40 @@
public TetheringRequest build() {
return new TetheringRequest(mBuilderParcel);
}
+ }
- /** Get static server address. */
- @Nullable
- public LinkAddress getLocalIpv4Address() {
- return mBuilderParcel.localIPv4Address;
- }
+ /**
+ * Get the local IPv4 address, if one was configured with
+ * {@link Builder#setStaticIpv4Addresses}.
+ */
+ @Nullable
+ public LinkAddress getLocalIpv4Address() {
+ return mRequestParcel.localIPv4Address;
+ }
- /** Get static client address. */
- @Nullable
- public LinkAddress getClientStaticIpv4Address() {
- return mBuilderParcel.staticClientAddress;
- }
+ /**
+ * Get the static IPv4 address of the client, if one was configured with
+ * {@link Builder#setStaticIpv4Addresses}.
+ */
+ @Nullable
+ public LinkAddress getClientStaticIpv4Address() {
+ return mRequestParcel.staticClientAddress;
+ }
+
+ /** Get tethering type. */
+ @TetheringType
+ public int getTetheringType() {
+ return mRequestParcel.tetheringType;
+ }
+
+ /** Check if exempt from entitlement check. */
+ public boolean isExemptFromEntitlementCheck() {
+ return mRequestParcel.exemptFromEntitlementCheck;
+ }
+
+ /** Check if show entitlement ui. */
+ public boolean getShouldShowEntitlementUi() {
+ return mRequestParcel.showProvisioningUi;
}
/**
@@ -602,18 +678,18 @@
/**
* Callback for use with {@link #startTethering} to find out whether tethering succeeded.
*/
- public abstract static class StartTetheringCallback {
+ public interface StartTetheringCallback {
/**
* Called when tethering has been successfully started.
*/
- public void onTetheringStarted() {}
+ default void onTetheringStarted() {}
/**
* Called when starting tethering failed.
*
- * @param resultCode One of the {@code TETHER_ERROR_*} constants.
+ * @param error The error that caused the failure.
*/
- public void onTetheringFailed(final int resultCode) {}
+ default void onTetheringFailed(@StartTetheringError final int error) {}
}
/**
@@ -663,11 +739,13 @@
* @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
* @param executor {@link Executor} to specify the thread upon which the callback of
* TetheringRequest will be invoked.
+ * @hide
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.TETHER_PRIVILEGED,
android.Manifest.permission.WRITE_SETTINGS
})
+ @SystemApi(client = MODULE_LIBRARIES)
public void startTethering(int type, @NonNull final Executor executor,
@NonNull final StartTetheringCallback callback) {
startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
@@ -684,7 +762,7 @@
android.Manifest.permission.TETHER_PRIVILEGED,
android.Manifest.permission.WRITE_SETTINGS
})
- public void stopTethering(final int type) {
+ public void stopTethering(@TetheringType final int type) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopTethering caller:" + callerPkg);
@@ -709,10 +787,10 @@
*
* @param resultCode an int value of entitlement result. It may be one of
* {@link #TETHER_ERROR_NO_ERROR},
- * {@link #TETHER_ERROR_PROVISION_FAILED}, or
+ * {@link #TETHER_ERROR_PROVISIONING_FAILED}, or
* {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
*/
- void onTetheringEntitlementResult(int resultCode);
+ void onTetheringEntitlementResult(@EntitlementResult int result);
}
/**
@@ -727,7 +805,8 @@
* fail if a tethering entitlement check is required.
*
* @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
- * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+ * @param showEntitlementUi a boolean indicating whether to check result for the UI-based
+ * entitlement check or the silent entitlement check.
* @param executor the executor on which callback will be invoked.
* @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
* notify the caller of the result of entitlement check. The listener may be called zero
@@ -737,7 +816,8 @@
android.Manifest.permission.TETHER_PRIVILEGED,
android.Manifest.permission.WRITE_SETTINGS
})
- public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
+ public void requestLatestTetheringEntitlementResult(@TetheringType int type,
+ boolean showEntitlementUi,
@NonNull Executor executor,
@NonNull final OnTetheringEntitlementResultListener listener) {
if (listener == null) {
@@ -766,7 +846,7 @@
*/
// TODO: improve the usage of ResultReceiver, b/145096122
@SystemApi(client = MODULE_LIBRARIES)
- public void requestLatestTetheringEntitlementResult(final int type,
+ public void requestLatestTetheringEntitlementResult(@TetheringType final int type,
@NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
@@ -779,7 +859,7 @@
* Callback for use with {@link registerTetheringEventCallback} to find out tethering
* upstream status.
*/
- public abstract static class TetheringEventCallback {
+ public interface TetheringEventCallback {
/**
* Called when tethering supported status changed.
*
@@ -791,7 +871,7 @@
*
* @param supported The new supported status
*/
- public void onTetheringSupported(boolean supported) {}
+ default void onTetheringSupported(boolean supported) {}
/**
* Called when tethering upstream changed.
@@ -802,7 +882,7 @@
* @param network the {@link Network} of tethering upstream. Null means tethering doesn't
* have any upstream.
*/
- public void onUpstreamChanged(@Nullable Network network) {}
+ default void onUpstreamChanged(@Nullable Network network) {}
/**
* Called when there was a change in tethering interface regular expressions.
@@ -810,28 +890,30 @@
* <p>This will be called immediately after the callback is registered, and may be called
* multiple times later upon changes.
* @param reg The new regular expressions.
- * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+ *
+ * @hide
*/
- @Deprecated
- public void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
+ @SystemApi(client = MODULE_LIBRARIES)
+ default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
/**
- * Called when there was a change in the list of tetherable interfaces.
+ * Called when there was a change in the list of tetherable interfaces. Tetherable
+ * interface means this interface is available and can be used for tethering.
*
* <p>This will be called immediately after the callback is registered, and may be called
* multiple times later upon changes.
- * @param interfaces The list of tetherable interfaces.
+ * @param interfaces The list of tetherable interface names.
*/
- public void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
+ default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
/**
* Called when there was a change in the list of tethered interfaces.
*
* <p>This will be called immediately after the callback is registered, and may be called
* multiple times later upon changes.
- * @param interfaces The list of tethered interfaces.
+ * @param interfaces The list of 0 or more String of currently tethered interface names.
*/
- public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
+ default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
/**
* Called when an error occurred configuring tethering.
@@ -841,7 +923,7 @@
* @param ifName Name of the interface.
* @param error One of {@code TetheringManager#TETHER_ERROR_*}.
*/
- public void onError(@NonNull String ifName, int error) {}
+ default void onError(@NonNull String ifName, @TetheringIfaceError int error) {}
/**
* Called when the list of tethered clients changes.
@@ -854,7 +936,7 @@
* determine if they are still connected.
* @param clients The new set of tethered clients; the collection is not ordered.
*/
- public void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
+ default void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
/**
* Called when tethering offload status changes.
@@ -862,19 +944,20 @@
* <p>This will be called immediately after the callback is registered.
* @param status The offload status.
*/
- public void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
+ default void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
}
/**
* Regular expressions used to identify tethering interfaces.
- * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+ * @hide
*/
- @Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public static class TetheringInterfaceRegexps {
private final String[] mTetherableBluetoothRegexs;
private final String[] mTetherableUsbRegexs;
private final String[] mTetherableWifiRegexs;
+ /** @hide */
public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs,
@NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) {
mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone();
diff --git a/Tethering/res/values-mcc204-mnc04/strings.xml b/Tethering/res/values-mcc204-mnc04/strings.xml
new file mode 100644
index 0000000..a996b42
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title">Hotspot has no internet</string>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_message">Devices can\u2019t connect to internet</string>
+ <!-- String for cellular roaming notification disable button [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_disable_button">Turn off hotspot</string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title">Hotspot is on</string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources>
diff --git a/Tethering/res/values-mcc310-mnc004/strings.xml b/Tethering/res/values-mcc310-mnc004/strings.xml
new file mode 100644
index 0000000..a996b42
--- /dev/null
+++ b/Tethering/res/values-mcc310-mnc004/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title">Hotspot has no internet</string>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_message">Devices can\u2019t connect to internet</string>
+ <!-- String for cellular roaming notification disable button [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_disable_button">Turn off hotspot</string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title">Hotspot is on</string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources>
diff --git a/Tethering/res/values-mcc310-mnc120/strings.xml b/Tethering/res/values-mcc310-mnc120/strings.xml
new file mode 100644
index 0000000..618df90
--- /dev/null
+++ b/Tethering/res/values-mcc310-mnc120/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- String for tethered notification title with client number info. -->
+ <plurals name="tethered_notification_title_with_client_number">
+ <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item>
+ <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item>
+ </plurals>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc311-mnc480/strings.xml b/Tethering/res/values-mcc311-mnc480/strings.xml
new file mode 100644
index 0000000..a996b42
--- /dev/null
+++ b/Tethering/res/values-mcc311-mnc480/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title">Hotspot has no internet</string>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_message">Devices can\u2019t connect to internet</string>
+ <!-- String for cellular roaming notification disable button [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_disable_button">Turn off hotspot</string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title">Hotspot is on</string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources>
diff --git a/Tethering/res/values-mcc311-mnc490/strings.xml b/Tethering/res/values-mcc311-mnc490/strings.xml
new file mode 100644
index 0000000..618df90
--- /dev/null
+++ b/Tethering/res/values-mcc311-mnc490/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- String for tethered notification title with client number info. -->
+ <plurals name="tethered_notification_title_with_client_number">
+ <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item>
+ <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item>
+ </plurals>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc312-mnc530/strings.xml b/Tethering/res/values-mcc312-mnc530/strings.xml
new file mode 100644
index 0000000..618df90
--- /dev/null
+++ b/Tethering/res/values-mcc312-mnc530/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- String for tethered notification title with client number info. -->
+ <plurals name="tethered_notification_title_with_client_number">
+ <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item>
+ <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item>
+ </plurals>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml
index ba98a66..52a1654 100644
--- a/Tethering/res/values/strings.xml
+++ b/Tethering/res/values/strings.xml
@@ -13,12 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Shown when the device is tethered -->
<!-- String for tethered notification title [CHAR LIMIT=200] -->
<string name="tethered_notification_title">Tethering or hotspot active</string>
<!-- String for tethered notification message [CHAR LIMIT=200] -->
<string name="tethered_notification_message">Tap to set up.</string>
+ <!-- String for tethered notification title with client number info. -->
+ <plurals name="tethered_notification_title_with_client_number">
+ </plurals>
<!-- This notification is shown when tethering has been disabled on a user's device.
The device is managed by the user's employer. Tethering can't be turned on unless the
@@ -32,4 +35,18 @@
Internet" settings page. That is currently the tether_settings_title_all string. -->
<!-- String for tether notification channel name [CHAR LIMIT=200] -->
<string name="notification_channel_tethering_status">Hotspot & tethering status</string>
-</resources>
\ No newline at end of file
+
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title"></string>
+ <!-- String for no upstream notification message [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_message"></string>
+ <!-- String for cellular roaming notification disable button [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_disable_button"></string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title"></string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message"></string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button"></string>
+</resources>
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 433b903..c5478d2 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -1054,7 +1054,7 @@
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
- mLastError = TetheringManager.TETHER_ERROR_MASTER_ERROR;
+ mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
transitionTo(mInitialState);
break;
default:
@@ -1185,7 +1185,7 @@
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Exception enabling NAT: " + e.toString());
cleanupUpstream();
- mLastError = TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+ mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
transitionTo(mInitialState);
return true;
}
diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index e81d6ac..bd60594 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -25,7 +25,7 @@
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -579,7 +579,7 @@
switch (value) {
case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
- case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED";
+ case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED";
default:
return String.format("UNKNOWN ERROR (%d)", value);
}
@@ -592,7 +592,7 @@
protected void onReceiveResult(int resultCode, Bundle resultData) {
int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
addDownstreamMapping(type, updatedCacheValue);
- if (updatedCacheValue == TETHER_ERROR_PROVISION_FAILED && notifyFail) {
+ if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) {
mListener.onUiEntitlementFailed(type);
}
if (receiver != null) receiver.send(updatedCacheValue, null);
@@ -635,8 +635,8 @@
mEntitlementCacheValue.put(type, resultCode);
return resultCode;
} else {
- mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
- return TETHER_ERROR_PROVISION_FAILED;
+ mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED);
+ return TETHER_ERROR_PROVISIONING_FAILED;
}
}
diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 0be162c..343ed4b 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -39,11 +39,12 @@
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -302,31 +303,26 @@
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
- mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+ mTetheringRestriction = new UserRestrictionActionListener(
+ userManager, this, mNotificationUpdater);
mExecutor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
+ mNetdCallback = new NetdCallback();
// Load tethering configuration.
updateConfiguration();
- // NetdCallback should be registered after updateConfiguration() to ensure
- // TetheringConfiguration is created.
- mNetdCallback = new NetdCallback();
+ }
+
+ /**
+ * Start to register callbacks.
+ * Call this function when tethering is ready to handle callback events.
+ */
+ public void startStateMachineUpdaters() {
try {
mNetd.registerUnsolicitedEventListener(mNetdCallback);
} catch (RemoteException e) {
mLog.e("Unable to register netd UnsolicitedEventListener");
}
-
- startStateMachineUpdaters(mHandler);
- startTrackDefaultNetwork();
-
- final WifiManager wifiManager = getWifiManager();
- if (wifiManager != null) {
- wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
- }
- }
-
- private void startStateMachineUpdaters(Handler handler) {
mCarrierConfigChange.startListening();
mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,
PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
@@ -339,7 +335,14 @@
filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter, null, handler);
+ mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
+
+ final WifiManager wifiManager = getWifiManager();
+ if (wifiManager != null) {
+ wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+ }
+
+ startTrackDefaultNetwork();
}
private class TetheringThreadExecutor implements Executor {
@@ -368,9 +371,10 @@
mActiveDataSubId = subId;
updateConfiguration();
+ mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId);
// To avoid launching unexpected provisioning checks, ignore re-provisioning
// when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
- // ill be triggered again when CarrierConfig is loaded.
+ // will be triggered again when CarrierConfig is loaded.
if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
} else {
@@ -543,7 +547,7 @@
break;
default:
Log.w(TAG, "Invalid tether type.");
- result = TETHER_ERROR_UNKNOWN_IFACE;
+ result = TETHER_ERROR_UNKNOWN_TYPE;
}
// The result of Bluetooth tethering will be sent by #setBluetoothTethering.
@@ -584,7 +588,7 @@
Binder.restoreCallingIdentity(ident);
}
- return TETHER_ERROR_MASTER_ERROR;
+ return TETHER_ERROR_INTERNAL_ERROR;
}
private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) {
@@ -620,7 +624,7 @@
// We should figure out a way to bubble up that failure instead of sending success.
final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
? TETHER_ERROR_NO_ERROR
- : TETHER_ERROR_MASTER_ERROR;
+ : TETHER_ERROR_INTERNAL_ERROR;
sendTetherResult(listener, result, TETHERING_BLUETOOTH);
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
@@ -632,7 +636,10 @@
Context.ETHERNET_SERVICE);
synchronized (mPublicSync) {
if (enable) {
- if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+ if (mEthernetCallback != null) {
+ Log.d(TAG, "Ethernet tethering already started");
+ return TETHER_ERROR_NO_ERROR;
+ }
mEthernetCallback = new EthernetCallback();
mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
@@ -993,11 +1000,14 @@
protected static class UserRestrictionActionListener {
private final UserManager mUserManager;
private final Tethering mWrapper;
+ private final TetheringNotificationUpdater mNotificationUpdater;
public boolean mDisallowTethering;
- public UserRestrictionActionListener(UserManager um, Tethering wrapper) {
+ public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
+ @NonNull TetheringNotificationUpdater updater) {
mUserManager = um;
mWrapper = wrapper;
+ mNotificationUpdater = updater;
mDisallowTethering = false;
}
@@ -1016,13 +1026,21 @@
return;
}
- // TODO: Add user restrictions notification.
- final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
-
- if (newlyDisallowed && isTetheringActiveOnDevice) {
- mWrapper.untetherAll();
- // TODO(b/148139325): send tetheringSupported on restriction change
+ if (!newlyDisallowed) {
+ // Clear the restricted notification when user is allowed to have tethering
+ // function.
+ mNotificationUpdater.tetheringRestrictionLifted();
+ return;
}
+
+ // Restricted notification is shown when tethering function is disallowed on
+ // user's device.
+ mNotificationUpdater.notifyTetheringDisabledByRestriction();
+
+ // Untether from all downstreams since tethering is disallowed.
+ mWrapper.untetherAll();
+
+ // TODO(b/148139325): send tetheringSupported on restriction change
}
}
@@ -1491,7 +1509,7 @@
} else {
dnsServers = mConfig.defaultIPv4DNS;
}
- final int netId = (network != null) ? network.netId : NETID_UNSET;
+ final int netId = (network != null) ? network.getNetId() : NETID_UNSET;
try {
mNetd.tetherDnsSet(netId, dnsServers);
mLog.log(String.format(
@@ -2182,7 +2200,7 @@
// If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
// Thus we give a chance for TetherMasterSM to recover to InitialState
// by sending CMD_CLEAR_ERROR
- if (error == TETHER_ERROR_MASTER_ERROR) {
+ if (error == TETHER_ERROR_INTERNAL_ERROR) {
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
}
int which;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
index b97f752..992cdd8 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
@@ -29,12 +29,14 @@
import android.content.res.Resources;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.ArrayRes;
import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
@@ -54,10 +56,15 @@
public class TetheringNotificationUpdater {
private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
private static final String CHANNEL_ID = "TETHERING_STATUS";
+ private static final String WIFI_DOWNSTREAM = "WIFI";
+ private static final String USB_DOWNSTREAM = "USB";
+ private static final String BLUETOOTH_DOWNSTREAM = "BT";
private static final boolean NOTIFY_DONE = true;
private static final boolean NO_NOTIFY = false;
// Id to update and cancel tethering notification. Must be unique within the tethering app.
- private static final int NOTIFY_ID = 20191115;
+ private static final int ENABLE_NOTIFICATION_ID = 1000;
+ // Id to update and cancel restricted notification. Must be unique within the tethering app.
+ private static final int RESTRICTED_NOTIFICATION_ID = 1001;
@VisibleForTesting
static final int NO_ICON_ID = 0;
@VisibleForTesting
@@ -65,14 +72,25 @@
private final Context mContext;
private final NotificationManager mNotificationManager;
private final NotificationChannel mChannel;
- // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
- // This value has to be made 1 2 and 4, and OR'd with the others.
+
// WARNING : the constructor is called on a different thread. Thread safety therefore
// relies on this value being initialized to 0, and not any other value. If you need
// to change this, you will need to change the thread where the constructor is invoked,
// or to introduce synchronization.
+ // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
+ // This value has to be made 1 2 and 4, and OR'd with the others.
private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+ // WARNING : this value is not able to being initialized to 0 and must have volatile because
+ // telephony service is not guaranteed that is up before tethering service starts. If telephony
+ // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid
+ // subscription id(0) to query resources. Therefore, initialized subscription id must be
+ // INVALID_SUBSCRIPTION_ID.
+ private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID})
+ @interface NotificationId {}
+
public TetheringNotificationUpdater(@NonNull final Context context) {
mContext = context;
mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
@@ -88,19 +106,46 @@
public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
if (mDownstreamTypesMask == downstreamTypesMask) return;
mDownstreamTypesMask = downstreamTypesMask;
- updateNotification();
+ updateEnableNotification();
}
- private void updateNotification() {
+ /** Called when active data subscription id changed */
+ public void onActiveDataSubscriptionIdChanged(final int subId) {
+ if (mActiveDataSubId == subId) return;
+ mActiveDataSubId = subId;
+ updateEnableNotification();
+ }
+
+ @VisibleForTesting
+ Resources getResourcesForSubId(@NonNull final Context c, final int subId) {
+ return SubscriptionManager.getResourcesForSubId(c, subId);
+ }
+
+ private void updateEnableNotification() {
final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
if (tetheringInactive || setupNotification() == NO_NOTIFY) {
- clearNotification();
+ clearNotification(ENABLE_NOTIFICATION_ID);
}
}
- private void clearNotification() {
- mNotificationManager.cancel(null /* tag */, NOTIFY_ID);
+ @VisibleForTesting
+ void tetheringRestrictionLifted() {
+ clearNotification(RESTRICTED_NOTIFICATION_ID);
+ }
+
+ private void clearNotification(@NotificationId final int id) {
+ mNotificationManager.cancel(null /* tag */, id);
+ }
+
+ @VisibleForTesting
+ void notifyTetheringDisabledByRestriction() {
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final String title = res.getString(R.string.disable_tether_notification_title);
+ final String message = res.getString(R.string.disable_tether_notification_message);
+
+ showNotification(R.drawable.stat_sys_tether_general, title, message,
+ RESTRICTED_NOTIFICATION_ID);
}
/**
@@ -110,16 +155,17 @@
*
* @return downstream types mask value.
*/
+ @VisibleForTesting
@IntRange(from = 0, to = 7)
- private int getDownstreamTypesMask(@NonNull final String types) {
+ int getDownstreamTypesMask(@NonNull final String types) {
int downstreamTypesMask = DOWNSTREAM_NONE;
final String[] downstreams = types.split("\\|");
for (String downstream : downstreams) {
- if ("USB".equals(downstream.trim())) {
+ if (USB_DOWNSTREAM.equals(downstream.trim())) {
downstreamTypesMask |= (1 << TETHERING_USB);
- } else if ("WIFI".equals(downstream.trim())) {
+ } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) {
downstreamTypesMask |= (1 << TETHERING_WIFI);
- } else if ("BT".equals(downstream.trim())) {
+ } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) {
downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
}
}
@@ -134,9 +180,8 @@
*
* @return {@link android.util.SparseArray} with downstream types and icon id info.
*/
- @NonNull
- private SparseArray<Integer> getIcons(@ArrayRes int id) {
- final Resources res = mContext.getResources();
+ @VisibleForTesting
+ SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) {
final String[] array = res.getStringArray(id);
final SparseArray<Integer> icons = new SparseArray<>();
for (String config : array) {
@@ -161,8 +206,9 @@
}
private boolean setupNotification() {
- final Resources res = mContext.getResources();
- final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons);
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final SparseArray<Integer> downstreamIcons =
+ getIcons(R.array.tethering_notification_icons, res);
final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID);
if (iconId == NO_ICON_ID) return NO_NOTIFY;
@@ -170,12 +216,12 @@
final String title = res.getString(R.string.tethering_notification_title);
final String message = res.getString(R.string.tethering_notification_message);
- showNotification(iconId, title, message);
+ showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID);
return NOTIFY_DONE;
}
private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
- @NonNull final String message) {
+ @NonNull final String message, @NotificationId final int id) {
final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
final PendingIntent pi = PendingIntent.getActivity(
mContext.createContextAsUser(UserHandle.CURRENT, 0),
@@ -193,6 +239,6 @@
.setContentIntent(pi)
.build();
- mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
+ mNotificationManager.notify(null /* tag */, id, notification);
}
}
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 020b32a..c30be25 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -33,6 +33,7 @@
import android.net.ITetheringEventCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.NetworkStack;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -79,6 +80,7 @@
mContext = mDeps.getContext();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mTethering = makeTethering(mDeps);
+ mTethering.startStateMachineUpdaters();
}
/**
@@ -364,8 +366,7 @@
IBinder connector;
try {
final long before = System.currentTimeMillis();
- while ((connector = (IBinder) mContext.getSystemService(
- Context.NETWORK_STACK_SERVICE)) == null) {
+ while ((connector = NetworkStack.getService()) == null) {
if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
return null;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 2875f71..7ac7f5f 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -244,7 +244,8 @@
// Additionally, we log a message to aid in any subsequent debugging.
mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
- cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
+ cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler,
+ mMobileNetworkCallback);
}
/** Release mobile network request. */
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 948266d..3106e0e 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -21,7 +21,7 @@
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
@@ -448,7 +448,7 @@
usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
usbTeardownOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+ mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR);
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 3a1d4a6..0a7850b 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -21,7 +21,7 @@
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -284,11 +284,11 @@
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 3. No cache value and ui entitlement check is needed.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+ assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
mCallbacklatch.countDown();
}
};
@@ -297,12 +297,13 @@
callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
- // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check.
+ // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
+ // check.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+ assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
mCallbacklatch.countDown();
}
};
@@ -311,7 +312,7 @@
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
- // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
+ // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
receiver = new ResultReceiver(null) {
@Override
@@ -364,7 +365,7 @@
public void verifyPermissionResult() {
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
@@ -380,15 +381,15 @@
public void verifyPermissionIfAllNotApproved() {
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
mLooper.dispatchAll();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
@@ -403,7 +404,7 @@
mLooper.dispatchAll();
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mLooper.dispatchAll();
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
assertTrue(mEnMgr.isCellularUpstreamPermitted());
@@ -465,7 +466,7 @@
assertEquals(0, mEnMgr.silentProvisionCount);
mEnMgr.reset();
// 6. switch upstream back to mobile again
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -477,7 +478,7 @@
public void testCallStopTetheringWhenUiProvisioningFail() {
setupForRequiredProvisioning();
verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt
new file mode 100644
index 0000000..b869491
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager.TETHERING_BLUETOOTH
+import android.net.ConnectivityManager.TETHERING_USB
+import android.net.ConnectivityManager.TETHERING_WIFI
+import android.os.UserHandle
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.test.BroadcastInterceptingContext
+import com.android.networkstack.tethering.R
+import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+const val TEST_SUBID = 1
+const val WIFI_ICON_ID = 1
+const val USB_ICON_ID = 2
+const val BT_ICON_ID = 3
+const val GENERAL_ICON_ID = 4
+const val WIFI_MASK = 1 shl TETHERING_WIFI
+const val USB_MASK = 1 shl TETHERING_USB
+const val BT_MASK = 1 shl TETHERING_BLUETOOTH
+const val TITTLE = "Tethering active"
+const val MESSAGE = "Tap here to set up."
+const val TEST_TITTLE = "Hotspot active"
+const val TEST_MESSAGE = "Tap to set up hotspot."
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TetheringNotificationUpdaterTest {
+ // lateinit used here for mocks as they need to be reinitialized between each test and the test
+ // should crash if they are used before being initialized.
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var notificationManager: NotificationManager
+ @Mock private lateinit var defaultResources: Resources
+ @Mock private lateinit var testResources: Resources
+
+ // lateinit for this class under test, as it should be reset to a different instance for every
+ // tests but should always be initialized before use (or the test should crash).
+ private lateinit var notificationUpdater: TetheringNotificationUpdater
+
+ private val ENABLE_ICON_CONFIGS = arrayOf(
+ "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth",
+ "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general",
+ "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general")
+
+ private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
+ override fun createContextAsUser(user: UserHandle, flags: Int) =
+ if (user == UserHandle.ALL) mockContext else this
+ }
+
+ private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) {
+ override fun getResourcesForSubId(context: Context, subId: Int) =
+ if (subId == TEST_SUBID) testResources else defaultResources
+ }
+
+ private fun setupResources() {
+ doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources)
+ .getStringArray(R.array.tethering_notification_icons)
+ doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources)
+ .getStringArray(R.array.tethering_notification_icons)
+ doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
+ doReturn(MESSAGE).`when`(defaultResources)
+ .getString(R.string.tethering_notification_message)
+ doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title)
+ doReturn(TEST_MESSAGE).`when`(testResources)
+ .getString(R.string.tethering_notification_message)
+ doReturn(USB_ICON_ID).`when`(defaultResources)
+ .getIdentifier(eq("android.test:drawable/usb"), any(), any())
+ doReturn(BT_ICON_ID).`when`(defaultResources)
+ .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any())
+ doReturn(GENERAL_ICON_ID).`when`(defaultResources)
+ .getIdentifier(eq("android.test:drawable/general"), any(), any())
+ doReturn(WIFI_ICON_ID).`when`(testResources)
+ .getIdentifier(eq("android.test:drawable/wifi"), any(), any())
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val context = TestContext(InstrumentationRegistry.getInstrumentation().context)
+ doReturn(notificationManager).`when`(mockContext)
+ .getSystemService(Context.NOTIFICATION_SERVICE)
+ notificationUpdater = WrappedNotificationUpdater(context)
+ setupResources()
+ }
+
+ private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
+ private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
+
+ private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") {
+ verify(notificationManager, never()).cancel(any(), anyInt())
+
+ val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
+ verify(notificationManager, times(1))
+ .notify(any(), anyInt(), notificationCaptor.capture())
+
+ val notification = notificationCaptor.getValue()
+ assertEquals(iconId, notification.smallIcon.resId)
+ assertEquals(title, notification.title())
+ assertEquals(text, notification.text())
+
+ reset(notificationManager)
+ }
+
+ private fun verifyNoNotification() {
+ verify(notificationManager, times(1)).cancel(any(), anyInt())
+ verify(notificationManager, never()).notify(any(), anyInt(), any())
+
+ reset(notificationManager)
+ }
+
+ @Test
+ fun testNotificationWithDownstreamChanged() {
+ // Wifi downstream. No notification.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyNoNotification()
+
+ // Same downstream changed. Nothing happened.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyZeroInteractions(notificationManager)
+
+ // Wifi and usb downstreams. Show enable notification
+ notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
+ verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE)
+
+ // Usb downstream. Still show enable notification.
+ notificationUpdater.onDownstreamChanged(USB_MASK)
+ verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+
+ // No downstream. No notification.
+ notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+ verifyNoNotification()
+ }
+
+ @Test
+ fun testNotificationWithActiveDataSubscriptionIdChanged() {
+ // Usb downstream. Showed enable notification with default resource.
+ notificationUpdater.onDownstreamChanged(USB_MASK)
+ verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+
+ // Same subId changed. Nothing happened.
+ notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
+ verifyZeroInteractions(notificationManager)
+
+ // Set test sub id. Clear notification with test resource.
+ notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+ verifyNoNotification()
+
+ // Wifi downstream. Show enable notification with test resource.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE)
+
+ // No downstream. No notification.
+ notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+ verifyNoNotification()
+ }
+
+ private fun assertIconNumbers(number: Int, configs: Array<String?>) {
+ doReturn(configs).`when`(defaultResources)
+ .getStringArray(R.array.tethering_notification_icons)
+ assertEquals(number, notificationUpdater.getIcons(
+ R.array.tethering_notification_icons, defaultResources).size())
+ }
+
+ @Test
+ fun testGetIcons() {
+ assertIconNumbers(0, arrayOfNulls<String>(0))
+ assertIconNumbers(0, arrayOf(null, ""))
+ assertIconNumbers(3, arrayOf(
+ // These configurations are invalid with wrong strings or symbols.
+ ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT",
+ "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test",
+ // This configuration is valid with two downstream types (USB, BT).
+ "USB|,,,,,|BT;drawable/test ",
+ // This configuration is valid with one downstream types (WIFI).
+ " WIFI ; android.test:drawable/xxx "))
+ }
+
+ @Test
+ fun testGetDownstreamTypesMask() {
+ assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask(""))
+ assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1"))
+ assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P"))
+ assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb"))
+ assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI "))
+ assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T"))
+ assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT"))
+ assertEquals(WIFI_MASK or USB_MASK,
+ notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||"))
+ }
+
+ @Test
+ fun testSetupRestrictedNotification() {
+ val title = InstrumentationRegistry.getInstrumentation().context.resources
+ .getString(R.string.disable_tether_notification_title)
+ val message = InstrumentationRegistry.getInstrumentation().context.resources
+ .getString(R.string.disable_tether_notification_message)
+ val disallowTitle = "Tether function is disallowed"
+ val disallowMessage = "Please contact your admin"
+ doReturn(title).`when`(defaultResources)
+ .getString(R.string.disable_tether_notification_title)
+ doReturn(message).`when`(defaultResources)
+ .getString(R.string.disable_tether_notification_message)
+ doReturn(disallowTitle).`when`(testResources)
+ .getString(R.string.disable_tether_notification_title)
+ doReturn(disallowMessage).`when`(testResources)
+ .getString(R.string.disable_tether_notification_message)
+
+ // User restrictions on. Show restricted notification.
+ notificationUpdater.notifyTetheringDisabledByRestriction()
+ verifyNotification(R.drawable.stat_sys_tether_general, title, message)
+
+ // User restrictions off. Clear notification.
+ notificationUpdater.tetheringRestrictionLifted()
+ verifyNoNotification()
+
+ // Set test sub id. No notification.
+ notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+ verifyNoNotification()
+
+ // User restrictions on again. Show restricted notification with test resource.
+ notificationUpdater.notifyTetheringDisabledByRestriction()
+ verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage)
+ }
+}
\ No newline at end of file
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index d983fae..7252737 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -210,7 +210,6 @@
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
-
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
super(base);
@@ -485,6 +484,7 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTethering = makeTethering();
+ mTethering.startStateMachineUpdaters();
verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
verify(mNetd).registerUnsolicitedEventListener(any());
final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
@@ -1073,13 +1073,15 @@
when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
final Tethering.UserRestrictionActionListener ural =
- new Tethering.UserRestrictionActionListener(mUserManager, mockTethering);
+ new Tethering.UserRestrictionActionListener(
+ mUserManager, mockTethering, mNotificationUpdater);
ural.mDisallowTethering = currentDisallow;
ural.onUserRestrictionsChanged();
- verify(mockTethering, times(expectedInteractionsWithShowNotification))
- .untetherAll();
+ verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
+ .notifyTetheringDisabledByRestriction();
+ verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
}
@Test
@@ -1087,7 +1089,7 @@
final String[] emptyActiveIfacesList = new String[]{};
final boolean currDisallow = false;
final boolean nextDisallow = true;
- final int expectedInteractionsWithShowNotification = 0;
+ final int expectedInteractionsWithShowNotification = 1;
runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList,
expectedInteractionsWithShowNotification);
@@ -1399,6 +1401,7 @@
mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
assertEquals(fakeSubId, newConfig.activeDataSubId);
+ verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId));
}
@Test
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 5ed75bf..7c98f62 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -212,8 +212,8 @@
mUNM.updateMobileRequiresDun(true);
mUNM.registerMobileNetworkRequest();
verify(mCM, times(1)).requestNetwork(
- any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
- any(Handler.class));
+ any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class),
+ any(NetworkCallback.class));
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
@@ -649,8 +649,8 @@
}
@Override
- public void requestNetwork(NetworkRequest req, NetworkCallback cb,
- int timeoutMs, int legacyType, Handler h) {
+ public void requestNetwork(NetworkRequest req,
+ int timeoutMs, int legacyType, Handler h, NetworkCallback cb) {
assertFalse(allCallbacks.containsKey(cb));
allCallbacks.put(cb, h);
assertFalse(requested.containsKey(cb));