Merge "Define stub and data classes for SharedConnectivity Service"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d0c000e..4a0b2eb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9783,6 +9783,133 @@
}
+package android.net.wifi.sharedconnectivity.app {
+
+ public final class DeviceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=100) @NonNull public int getBatteryPercentage();
+ method @IntRange(from=0, to=3) @NonNull public int getConnectionStrength();
+ method @NonNull public String getDeviceName();
+ method public int getDeviceType();
+ method @NonNull public String getModelName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.DeviceInfo> CREATOR;
+ field public static final int DEVICE_TYPE_LAPTOP = 3; // 0x3
+ field public static final int DEVICE_TYPE_PHONE = 1; // 0x1
+ field public static final int DEVICE_TYPE_TABLET = 2; // 0x2
+ field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class DeviceInfo.Builder {
+ ctor public DeviceInfo.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setBatteryPercentage(@IntRange(from=0, to=100) int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setConnectionStrength(@IntRange(from=0, to=3) int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setDeviceName(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setDeviceType(int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setModelName(@NonNull String);
+ }
+
+ public final class KnownNetwork implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo();
+ method @NonNull public int getNetworkSource();
+ method @NonNull public int[] getSecurityTypes();
+ method @NonNull public String getSsid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.KnownNetwork> CREATOR;
+ field public static final int NETWORK_SOURCE_CLOUD_SELF = 1; // 0x1
+ field public static final int NETWORK_SOURCE_NEARBY_SELF = 0; // 0x0
+ }
+
+ public static final class KnownNetwork.Builder {
+ ctor public KnownNetwork.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSecurityTypes(@NonNull int[]);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String);
+ }
+
+ public interface SharedConnectivityClientCallback {
+ method public void onKnownNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork>);
+ method public void onSharedConnectivitySettingsChanged(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState);
+ method public void onTetherNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork>);
+ }
+
+ public class SharedConnectivityManager {
+ ctor public SharedConnectivityManager(@NonNull android.content.Context);
+ method public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
+ method public boolean disconnectTetherNetwork();
+ method public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method public boolean registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
+ method public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
+ }
+
+ public final class SharedConnectivitySettingsState implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public boolean isInstantTetherEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState> CREATOR;
+ }
+
+ public static final class SharedConnectivitySettingsState.Builder {
+ ctor public SharedConnectivitySettingsState.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.Builder setInstantTetherEnabled(boolean);
+ }
+
+ public final class TetherNetwork implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public long getDeviceId();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo();
+ method @Nullable public String getHotspotBssid();
+ method @Nullable public int[] getHotspotSecurityTypes();
+ method @Nullable public String getHotspotSsid();
+ method @NonNull public String getNetworkName();
+ method @NonNull public int getNetworkType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.TetherNetwork> CREATOR;
+ field public static final int NETWORK_TYPE_CELLULAR = 1; // 0x1
+ field public static final int NETWORK_TYPE_ETHERNET = 3; // 0x3
+ field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int NETWORK_TYPE_WIFI = 2; // 0x2
+ }
+
+ public static final class TetherNetwork.Builder {
+ ctor public TetherNetwork.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceId(long);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotBssid(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSecurityTypes(@NonNull int[]);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSsid(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkName(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkType(int);
+ }
+
+}
+
+package android.net.wifi.sharedconnectivity.service {
+
+ public abstract class SharedConnectivityService extends android.app.Service {
+ ctor public SharedConnectivityService();
+ ctor public SharedConnectivityService(@NonNull android.os.Handler);
+ method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method public abstract void onConnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
+ method public abstract void onDisconnectTetherNetwork();
+ method public abstract void onForgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method public final void setKnownNetworks(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork>);
+ method public final void setSettingsState(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState);
+ method public final void setTetherNetworks(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork>);
+ }
+
+}
+
package android.nfc {
public final class NfcAdapter {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2f5e820..b3e3f35 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1863,6 +1863,14 @@
}
+package android.net.wifi.sharedconnectivity.app {
+
+ public class SharedConnectivityManager {
+ method public void setService(@Nullable android.os.IInterface);
+ }
+
+}
+
package android.os {
public final class BatteryStatsManager {
diff --git a/wifi/java/Android.bp b/wifi/java/Android.bp
index 225e750..434226d 100644
--- a/wifi/java/Android.bp
+++ b/wifi/java/Android.bp
@@ -27,7 +27,10 @@
filegroup {
name: "framework-wifi-non-updatable-sources-internal",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
path: "src",
visibility: ["//visibility:private"],
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.aidl
new file mode 100644
index 0000000..35d5c15
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+parcelable DeviceInfo;
\ No newline at end of file
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.java
new file mode 100644
index 0000000..7874b2a
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A data class representing a device providing connectivity.
+ * This class is used in IPC calls between the implementer of {@link SharedConnectivityService} and
+ * the consumers of {@link com.android.wifitrackerlib}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DeviceInfo implements Parcelable {
+
+ /**
+ * Device type providing connectivity is unknown.
+ */
+ public static final int DEVICE_TYPE_UNKNOWN = 0;
+
+ /**
+ * Device providing connectivity is a mobile phone.
+ */
+ public static final int DEVICE_TYPE_PHONE = 1;
+
+ /**
+ * Device providing connectivity is a tablet.
+ */
+ public static final int DEVICE_TYPE_TABLET = 2;
+
+ /**
+ * Device providing connectivity is a laptop.
+ */
+ public static final int DEVICE_TYPE_LAPTOP = 3;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DEVICE_TYPE_UNKNOWN,
+ DEVICE_TYPE_PHONE,
+ DEVICE_TYPE_TABLET,
+ DEVICE_TYPE_LAPTOP
+ })
+ public @interface DeviceType {}
+
+ @DeviceType private final int mDeviceType;
+ private final String mDeviceName;
+ private final String mModelName;
+ private final int mBatteryPercentage;
+ private final int mConnectionStrength;
+
+ /**
+ * Builder class for {@link DeviceInfo}.
+ */
+ public static final class Builder {
+ private int mDeviceType;
+ private String mDeviceName;
+ private String mModelName;
+ private int mBatteryPercentage;
+ private int mConnectionStrength;
+
+ public Builder() {}
+
+ /**
+ * Sets the device type that provides connectivity.
+ *
+ * @param deviceType Device type as represented by IntDef {@link DeviceType}.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setDeviceType(@DeviceType int deviceType) {
+ mDeviceType = deviceType;
+ return this;
+ }
+
+ /**
+ * Sets the device name of the remote device.
+ *
+ * @param deviceName The user configurable device name.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setDeviceName(@NonNull String deviceName) {
+ mDeviceName = deviceName;
+ return this;
+ }
+
+ /**
+ * Sets the model name of the remote device.
+ *
+ * @param modelName The OEM configured name for the device model.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setModelName(@NonNull String modelName) {
+ mModelName = modelName;
+ return this;
+ }
+
+ /**
+ * Sets the battery charge percentage of the remote device.
+ *
+ * @param batteryPercentage The battery charge percentage in the range 0 to 100.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setBatteryPercentage(@IntRange(from = 0, to = 100) int batteryPercentage) {
+ mBatteryPercentage = batteryPercentage;
+ return this;
+ }
+
+ /**
+ * Sets the displayed connection strength of the remote device to the internet.
+ *
+ * @param connectionStrength Connection strength in range 0 to 3.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setConnectionStrength(@IntRange(from = 0, to = 3) int connectionStrength) {
+ mConnectionStrength = connectionStrength;
+ return this;
+ }
+
+ /**
+ * Builds the {@link DeviceInfo} object.
+ *
+ * @return Returns the built {@link DeviceInfo} object.
+ */
+ @NonNull
+ public DeviceInfo build() {
+ return new DeviceInfo(mDeviceType, mDeviceName, mModelName, mBatteryPercentage,
+ mConnectionStrength);
+ }
+ }
+
+ private static void validate(int deviceType, String deviceName, String modelName,
+ int batteryPercentage, int connectionStrength) {
+ if (deviceType != DEVICE_TYPE_UNKNOWN && deviceType != DEVICE_TYPE_PHONE
+ && deviceType != DEVICE_TYPE_TABLET && deviceType != DEVICE_TYPE_LAPTOP) {
+ throw new IllegalArgumentException("Illegal device type");
+ }
+ if (Objects.isNull(deviceName)) {
+ throw new IllegalArgumentException("DeviceName must be set");
+ }
+ if (Objects.isNull(modelName)) {
+ throw new IllegalArgumentException("ModelName must be set");
+ }
+ if (batteryPercentage < 0 || batteryPercentage > 100) {
+ throw new IllegalArgumentException("BatteryPercentage must be in range 0-100");
+ }
+ if (connectionStrength < 0 || connectionStrength > 3) {
+ throw new IllegalArgumentException("ConnectionStrength must be in range 0-3");
+ }
+ }
+
+ private DeviceInfo(@DeviceType int deviceType, @NonNull String deviceName,
+ @NonNull String modelName, int batteryPercentage, int connectionStrength) {
+ validate(deviceType, deviceName, modelName, batteryPercentage, connectionStrength);
+ mDeviceType = deviceType;
+ mDeviceName = deviceName;
+ mModelName = modelName;
+ mBatteryPercentage = batteryPercentage;
+ mConnectionStrength = connectionStrength;
+ }
+
+ /**
+ * Gets the device type that provides connectivity.
+ *
+ * @return Returns the device type as represented by IntDef {@link DeviceType}.
+ */
+ @DeviceType
+ public int getDeviceType() {
+ return mDeviceType;
+ }
+
+ /**
+ * Gets the device name of the remote device.
+ *
+ * @return Returns the user configurable device name.
+ */
+ @NonNull
+ public String getDeviceName() {
+ return mDeviceName;
+ }
+
+ /**
+ * Gets the model name of the remote device.
+ *
+ * @return Returns the OEM configured name for the device model.
+ */
+ @NonNull
+ public String getModelName() {
+ return mModelName;
+ }
+
+ /**
+ * Gets the battery charge percentage of the remote device.
+ *
+ * @return Returns the battery charge percentage in the range 0 to 100.
+ */
+ @NonNull
+ @IntRange(from = 0, to = 100)
+ public int getBatteryPercentage() {
+ return mBatteryPercentage;
+ }
+
+ /**
+ * Gets the displayed connection strength of the remote device to the internet.
+ *
+ * @return Returns the connection strength in range 0 to 3.
+ */
+ @NonNull
+ @IntRange(from = 0, to = 3)
+ public int getConnectionStrength() {
+ return mConnectionStrength;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DeviceInfo)) return false;
+ DeviceInfo other = (DeviceInfo) obj;
+ return mDeviceType == other.getDeviceType()
+ && Objects.equals(mDeviceName, other.mDeviceName)
+ && Objects.equals(mModelName, other.mModelName)
+ && mBatteryPercentage == other.mBatteryPercentage
+ && mConnectionStrength == other.mConnectionStrength;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDeviceType, mDeviceName, mModelName, mBatteryPercentage,
+ mConnectionStrength);
+ }
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mDeviceType);
+ dest.writeString(mDeviceName);
+ dest.writeString(mModelName);
+ dest.writeInt(mBatteryPercentage);
+ dest.writeInt(mConnectionStrength);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<DeviceInfo> CREATOR = new Creator<DeviceInfo>() {
+ @Override
+ public DeviceInfo createFromParcel(Parcel in) {
+ return new DeviceInfo(in.readInt(), in.readString(), in.readString(), in.readInt(),
+ in.readInt());
+ }
+
+ @Override
+ public DeviceInfo[] newArray(int size) {
+ return new DeviceInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder("DeviceInfo[")
+ .append("deviceType=").append(mDeviceType)
+ .append(", deviceName=").append(mDeviceName)
+ .append(", modelName=").append(mModelName)
+ .append(", batteryPercentage=").append(mBatteryPercentage)
+ .append(", connectionStrength=").append(mConnectionStrength)
+ .append("]").toString();
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.aidl
new file mode 100644
index 0000000..140d72a
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+parcelable KnownNetwork;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
new file mode 100644
index 0000000..34b7e94
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static android.net.wifi.WifiAnnotations.SecurityType;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A data class representing a known Wifi network.
+ *
+ * @hide
+ */
+@SystemApi
+public final class KnownNetwork implements Parcelable {
+ /**
+ * Network is known by a nearby device with the same user account.
+ */
+ public static final int NETWORK_SOURCE_NEARBY_SELF = 0;
+
+ /**
+ * Network is known via cloud storage associated with this device's user account.
+ */
+ public static final int NETWORK_SOURCE_CLOUD_SELF = 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ NETWORK_SOURCE_NEARBY_SELF,
+ NETWORK_SOURCE_CLOUD_SELF
+ })
+ public @interface NetworkSource {}
+
+ @NetworkSource private final int mNetworkSource;
+ private final String mSsid;
+ @SecurityType private final int[] mSecurityTypes;
+ private final DeviceInfo mDeviceInfo;
+
+ /**
+ * Builder class for {@link KnownNetwork}.
+ */
+ public static final class Builder {
+ @NetworkSource private int mNetworkSource = -1;
+ private String mSsid;
+ @SecurityType private int[] mSecurityTypes;
+ private android.net.wifi.sharedconnectivity.app.DeviceInfo mDeviceInfo;
+
+ public Builder() {}
+
+ /**
+ * Sets the indicated source of the known network.
+ *
+ * @param networkSource The network source as defined by IntDef {@link NetworkSource}.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setNetworkSource(@NetworkSource int networkSource) {
+ mNetworkSource = networkSource;
+ return this;
+ }
+
+ /**
+ * Sets the SSID of the known network.
+ *
+ * @param ssid The SSID of the known network. Surrounded by double quotes if UTF-8.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setSsid(@NonNull String ssid) {
+ mSsid = ssid;
+ return this;
+ }
+
+ /**
+ * Sets the security types of the known network.
+ *
+ * @param securityTypes The array of security types supported by the known network.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setSecurityTypes(@NonNull @SecurityType int[] securityTypes) {
+ mSecurityTypes = securityTypes;
+ return this;
+ }
+
+ /**
+ * Sets the device information of the device providing connectivity.
+ *
+ * @param deviceInfo The array of security types supported by the known network.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setDeviceInfo(@NonNull DeviceInfo deviceInfo) {
+ mDeviceInfo = deviceInfo;
+ return this;
+ }
+
+ /**
+ * Builds the {@link KnownNetwork} object.
+ *
+ * @return Returns the built {@link KnownNetwork} object.
+ */
+ @NonNull
+ public KnownNetwork build() {
+ return new KnownNetwork(
+ mNetworkSource,
+ mSsid,
+ mSecurityTypes,
+ mDeviceInfo);
+ }
+ }
+
+ private static void validate(int networkSource, String ssid, int [] securityTypes) {
+ if (networkSource != NETWORK_SOURCE_CLOUD_SELF && networkSource
+ != NETWORK_SOURCE_NEARBY_SELF) {
+ throw new IllegalArgumentException("Illegal network source");
+ }
+ if (TextUtils.isEmpty(ssid)) {
+ throw new IllegalArgumentException("SSID must be set");
+ }
+ if (securityTypes == null || securityTypes.length == 0) {
+ throw new IllegalArgumentException("SecurityTypes must be set");
+ }
+ }
+
+ private KnownNetwork(
+ @NetworkSource int networkSource,
+ @NonNull String ssid,
+ @NonNull @SecurityType int[] securityTypes,
+ @NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo deviceInfo) {
+ validate(networkSource, ssid, securityTypes);
+ mNetworkSource = networkSource;
+ mSsid = ssid;
+ mSecurityTypes = securityTypes;
+ mDeviceInfo = deviceInfo;
+ }
+
+ /**
+ * Gets the indicated source of the known network.
+ *
+ * @return Returns the network source as defined by IntDef {@link NetworkSource}.
+ */
+ @NonNull
+ @NetworkSource
+ public int getNetworkSource() {
+ return mNetworkSource;
+ }
+
+ /**
+ * Gets the SSID of the known network.
+ *
+ * @return Returns the SSID of the known network. Surrounded by double quotes if UTF-8.
+ */
+ @NonNull
+ public String getSsid() {
+ return mSsid;
+ }
+
+ /**
+ * Gets the security types of the known network.
+ *
+ * @return Returns the array of security types supported by the known network.
+ */
+ @NonNull
+ @SecurityType
+ public int[] getSecurityTypes() {
+ return mSecurityTypes;
+ }
+
+ /**
+ * Gets the device information of the device providing connectivity.
+ *
+ * @return Returns the array of security types supported by the known network.
+ */
+ @NonNull
+ public DeviceInfo getDeviceInfo() {
+ return mDeviceInfo;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof KnownNetwork)) return false;
+ KnownNetwork other = (KnownNetwork) obj;
+ return mNetworkSource == other.getNetworkSource()
+ && Objects.equals(mSsid, other.getSsid())
+ && Arrays.equals(mSecurityTypes, other.getSecurityTypes())
+ && Objects.equals(mDeviceInfo, other.getDeviceInfo());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetworkSource, mSsid, Arrays.hashCode(mSecurityTypes),
+ mDeviceInfo.hashCode());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mNetworkSource);
+ dest.writeString(mSsid);
+ dest.writeIntArray(mSecurityTypes);
+ dest.writeTypedObject(mDeviceInfo, 0);
+ }
+
+ @NonNull
+ public static final Creator<KnownNetwork> CREATOR = new Creator<>() {
+ @Override
+ public KnownNetwork createFromParcel(Parcel in) {
+ return new KnownNetwork(in.readInt(), in.readString(), in.createIntArray(),
+ in.readTypedObject(DeviceInfo.CREATOR));
+ }
+
+ @Override
+ public KnownNetwork[] newArray(int size) {
+ return new KnownNetwork[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder("KnownNetwork[")
+ .append("NetworkSource=").append(mNetworkSource)
+ .append(", ssid=").append(mSsid)
+ .append(", securityTypes=").append(Arrays.toString(mSecurityTypes))
+ .append(", deviceInfo=").append(mDeviceInfo.toString())
+ .append("]").toString();
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
new file mode 100644
index 0000000..dcb5201
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
+
+import java.util.List;
+
+/**
+ * Interface for clients of {@link SharedConnectivityManager} to register for changes in network
+ * status.
+ *
+ * @hide
+ */
+@SystemApi
+public interface SharedConnectivityClientCallback {
+ /**
+ * This method is being called by {@link SharedConnectivityService} to notify of a change in the
+ * list of available Tether Networks.
+ * @param networks Updated Tether Network list.
+ */
+ void onTetherNetworksUpdated(@NonNull List<TetherNetwork> networks);
+
+ /**
+ * This method is being called by {@link SharedConnectivityService} to notify of a change in the
+ * list of available Known Networks.
+ * @param networks Updated Known Network list.
+ */
+ void onKnownNetworksUpdated(@NonNull List<KnownNetwork> networks);
+
+ /**
+ * This method is being called by {@link SharedConnectivityService} to notify of a change in the
+ * state of share connectivity settings.
+ * @param state The new state.
+ */
+ void onSharedConnectivitySettingsChanged(@NonNull SharedConnectivitySettingsState state);
+}
+
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
new file mode 100644
index 0000000..b43e4f7
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.res.Resources;
+import android.net.wifi.sharedconnectivity.service.ISharedConnectivityCallback;
+import android.net.wifi.sharedconnectivity.service.ISharedConnectivityService;
+import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * This class is the library used by consumers of Shared Connectivity data to bind to the service,
+ * receive callbacks from, and send user actions to the service.
+ *
+ * @hide
+ */
+@SystemApi
+public class SharedConnectivityManager {
+ private static final String TAG = SharedConnectivityManager.class.getSimpleName();
+ private static final boolean DEBUG = true;
+ private static final String SERVICE_PACKAGE_NAME = "sharedconnectivity_service_package";
+ private static final String SERVICE_CLASS_NAME = "sharedconnectivity_service_class";
+
+ private static final class SharedConnectivityCallbackProxy extends
+ ISharedConnectivityCallback.Stub {
+ private final Executor mExecutor;
+ private final SharedConnectivityClientCallback mCallback;
+
+ SharedConnectivityCallbackProxy(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull SharedConnectivityClientCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onTetherNetworksUpdated(@NonNull List<TetherNetwork> networks) {
+ if (mCallback != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onTetherNetworksUpdated(networks));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public void onKnownNetworksUpdated(@NonNull List<KnownNetwork> networks) {
+ if (mCallback != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onKnownNetworksUpdated(networks));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public void onSharedConnectivitySettingsChanged(
+ @NonNull SharedConnectivitySettingsState state) {
+ if (mCallback != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onSharedConnectivitySettingsChanged(state));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+ }
+
+ private ISharedConnectivityService mService;
+ private final Map<SharedConnectivityClientCallback, SharedConnectivityCallbackProxy>
+ mProxyMap = new HashMap<>();
+
+ /**
+ * Constructor for new instance of {@link SharedConnectivityManager}.
+ *
+ * Automatically binds to implementation of {@link SharedConnectivityService} specified in
+ * device overlay.
+ */
+ @SuppressLint("ManagerConstructor")
+ public SharedConnectivityManager(@NonNull Context context) {
+ ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = ISharedConnectivityService.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.i(TAG, "onServiceDisconnected");
+ mService = null;
+ mProxyMap.clear();
+ }
+ };
+ bind(context, serviceConnection);
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public void setService(@Nullable IInterface service) {
+ mService = (ISharedConnectivityService) service;
+ }
+
+ private void bind(Context context, ServiceConnection serviceConnection) {
+ Resources resources = context.getResources();
+ int packageNameId = resources.getIdentifier(SERVICE_PACKAGE_NAME, "string",
+ context.getPackageName());
+ int classNameId = resources.getIdentifier(SERVICE_CLASS_NAME, "string",
+ context.getPackageName());
+ if (packageNameId == 0 || classNameId == 0) {
+ throw new Resources.NotFoundException("Package and class names for"
+ + " shared connectivity service must be defined");
+ }
+
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(resources.getString(packageNameId),
+ resources.getString(classNameId)));
+ context.bindService(
+ intent,
+ serviceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Registers a callback for receiving updates to the list of Tether Networks and Known Networks.
+ *
+ * @param executor The Executor used to invoke the callback.
+ * @param callback The callback of type {@link SharedConnectivityClientCallback} that is invoked
+ * when the service updates either the list of Tether Networks or Known
+ * Networks.
+ * @return Returns true if the registration was successful, false otherwise.
+ */
+ public boolean registerCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull SharedConnectivityClientCallback callback) {
+ Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(callback, "callback cannot be null");
+ if (mService == null || mProxyMap.containsKey(callback)) return false;
+ try {
+ SharedConnectivityCallbackProxy proxy =
+ new SharedConnectivityCallbackProxy(executor, callback);
+ mService.registerCallback(proxy);
+ mProxyMap.put(callback, proxy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in registerCallback", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Unregisters a callback.
+ *
+ * @return Returns true if the callback was successfully unregistered, false otherwise.
+ */
+ public boolean unregisterCallback(
+ @NonNull SharedConnectivityClientCallback callback) {
+ Objects.requireNonNull(callback, "callback cannot be null");
+ if (mService == null || !mProxyMap.containsKey(callback)) return false;
+ try {
+ mService.unregisterCallback(mProxyMap.get(callback));
+ mProxyMap.remove(callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in unregisterCallback", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Send command to the implementation of {@link SharedConnectivityService} requesting connection
+ * to the specified Tether Network.
+ *
+ * @param network {@link TetherNetwork} object representing the network the user has requested
+ * a connection to.
+ * @return Returns true if the service received the command. Does not guarantee that the
+ * connection was successful.
+ */
+ public boolean connectTetherNetwork(@NonNull TetherNetwork network) {
+ if (mService == null) return false;
+ try {
+ mService.connectTetherNetwork(network);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in connectTetherNetwork", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Send command to the implementation of {@link SharedConnectivityService} requesting
+ * disconnection from the active Tether Network.
+ *
+ * @return Returns true if the service received the command. Does not guarantee that the
+ * disconnection was successful.
+ */
+ public boolean disconnectTetherNetwork() {
+ if (mService == null) return false;
+ try {
+ mService.disconnectTetherNetwork();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in disconnectTetherNetwork", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Send command to the implementation of {@link SharedConnectivityService} requesting connection
+ * to the specified Known Network.
+ *
+ * @param network {@link KnownNetwork} object representing the network the user has requested
+ * a connection to.
+ * @return Returns true if the service received the command. Does not guarantee that the
+ * connection was successful.
+ */
+ public boolean connectKnownNetwork(@NonNull KnownNetwork network) {
+ if (mService == null) return false;
+ try {
+ mService.connectKnownNetwork(network);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in connectKnownNetwork", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Send command to the implementation of {@link SharedConnectivityService} requesting removal of
+ * the specified Known Network from the list of Known Networks.
+ *
+ * @return Returns true if the service received the command. Does not guarantee that the
+ * forget action was successful.
+ */
+ public boolean forgetKnownNetwork(@NonNull KnownNetwork network) {
+ if (mService == null) return false;
+ try {
+ mService.forgetKnownNetwork(network);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in forgetKnownNetwork", e);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.aidl
new file mode 100644
index 0000000..289afac
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+parcelable SharedConnectivitySettingsState;
\ No newline at end of file
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java
new file mode 100644
index 0000000..dd2fa94
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * A data class representing the shared connectivity settings state.
+ *
+ * This class represents a snapshot of the settings and can be out of date if the settings changed
+ * after receiving an object of this class.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SharedConnectivitySettingsState implements Parcelable {
+
+ private final boolean mInstantTetherEnabled;
+ private final Bundle mExtras;
+
+ /**
+ * Builder class for {@link SharedConnectivitySettingsState}.
+ */
+ public static final class Builder {
+ private boolean mInstantTetherEnabled;
+ private Bundle mExtras;
+
+ public Builder() {}
+
+ /**
+ * Sets the state of Instant Tether in settings
+ *
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setInstantTetherEnabled(boolean instantTetherEnabled) {
+ mInstantTetherEnabled = instantTetherEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the extras bundle
+ *
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Builds the {@link SharedConnectivitySettingsState} object.
+ *
+ * @return Returns the built {@link SharedConnectivitySettingsState} object.
+ */
+ @NonNull
+ public SharedConnectivitySettingsState build() {
+ return new SharedConnectivitySettingsState(mInstantTetherEnabled, mExtras);
+ }
+ }
+
+ private SharedConnectivitySettingsState(boolean instantTetherEnabled, Bundle extras) {
+ mInstantTetherEnabled = instantTetherEnabled;
+ mExtras = extras;
+ }
+
+ /**
+ * Gets the state of Instant Tether in settings
+ *
+ * @return Returns true for enabled, false otherwise.
+ */
+ @NonNull
+ public boolean isInstantTetherEnabled() {
+ return mInstantTetherEnabled;
+ }
+
+ /**
+ * Gets the extras Bundle.
+ *
+ * @return Returns a Bundle object.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SharedConnectivitySettingsState)) return false;
+ SharedConnectivitySettingsState other = (SharedConnectivitySettingsState) obj;
+ return mInstantTetherEnabled == other.isInstantTetherEnabled();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mInstantTetherEnabled);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mInstantTetherEnabled);
+ dest.writeBundle(mExtras);
+ }
+
+ @NonNull
+ public static final Creator<SharedConnectivitySettingsState> CREATOR =
+ new Creator<SharedConnectivitySettingsState>() {
+ @Override
+ public SharedConnectivitySettingsState createFromParcel(Parcel in) {
+ return new SharedConnectivitySettingsState(in.readBoolean(),
+ in.readBundle(getClass().getClassLoader()));
+ }
+
+ @Override
+ public SharedConnectivitySettingsState[] newArray(int size) {
+ return new SharedConnectivitySettingsState[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder("SharedConnectivitySettingsState[")
+ .append("instantTetherEnabled=").append(mInstantTetherEnabled)
+ .append("extras=").append(mExtras.toString())
+ .append("]").toString();
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
new file mode 100644
index 0000000..6cc4cfe
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+parcelable TetherNetwork;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
new file mode 100644
index 0000000..bbdad53
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static android.net.wifi.WifiAnnotations.SecurityType;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A data class representing an Instant Tether network.
+ * This class is used in IPC calls between the implementer of {@link SharedConnectivityService} and
+ * the consumers of {@link com.android.wifitrackerlib}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class TetherNetwork implements Parcelable {
+ /**
+ * Remote device is connected to the internet via an unknown connection.
+ */
+ public static final int NETWORK_TYPE_UNKNOWN = 0;
+
+ /**
+ * Remote device is connected to the internet via a cellular connection.
+ */
+ public static final int NETWORK_TYPE_CELLULAR = 1;
+
+ /**
+ * Remote device is connected to the internet via a Wi-Fi connection.
+ */
+ public static final int NETWORK_TYPE_WIFI = 2;
+
+ /**
+ * Remote device is connected to the internet via an ethernet connection.
+ */
+ public static final int NETWORK_TYPE_ETHERNET = 3;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ NETWORK_TYPE_UNKNOWN,
+ NETWORK_TYPE_CELLULAR,
+ NETWORK_TYPE_WIFI,
+ NETWORK_TYPE_ETHERNET
+ })
+ public @interface NetworkType {}
+
+ private final long mDeviceId;
+ private final DeviceInfo mDeviceInfo;
+ @NetworkType private final int mNetworkType;
+ private final String mNetworkName;
+ @Nullable private final String mHotspotSsid;
+ @Nullable private final String mHotspotBssid;
+ @Nullable @SecurityType private final int[] mHotspotSecurityTypes;
+
+ /**
+ * Builder class for {@link TetherNetwork}.
+ */
+ public static final class Builder {
+ private long mDeviceId = -1;
+ private DeviceInfo mDeviceInfo;
+ @NetworkType private int mNetworkType;
+ private String mNetworkName;
+ @Nullable private String mHotspotSsid;
+ @Nullable private String mHotspotBssid;
+ @Nullable @SecurityType private int[] mHotspotSecurityTypes;
+
+ public Builder() {}
+
+ /**
+ * Set the remote device ID.
+ *
+ * @param deviceId Locally unique ID for this Instant Tether network.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setDeviceId(long deviceId) {
+ mDeviceId = deviceId;
+ return this;
+ }
+
+ /**
+ * Sets information about the device providing connectivity.
+ *
+ * @param deviceInfo The user configurable device name.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setDeviceInfo(@NonNull DeviceInfo deviceInfo) {
+ mDeviceInfo = deviceInfo;
+ return this;
+ }
+
+ /**
+ * Sets the network type that the remote device is connected to.
+ *
+ * @param networkType Network type as represented by IntDef {@link NetworkType}.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setNetworkType(@NetworkType int networkType) {
+ mNetworkType = networkType;
+ return this;
+ }
+
+ /**
+ * Sets the display name of the network the remote device is connected to.
+ *
+ * @param networkName Network display name. (e.g. "Google Fi", "Hotel WiFi", "Ethernet")
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setNetworkName(@NonNull String networkName) {
+ mNetworkName = networkName;
+ return this;
+ }
+
+ /**
+ * Sets the hotspot SSID being broadcast by the remote device, or null if hotspot is off.
+ *
+ * @param hotspotSsid The SSID of the hotspot. Surrounded by double quotes if UTF-8.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setHotspotSsid(@NonNull String hotspotSsid) {
+ mHotspotSsid = hotspotSsid;
+ return this;
+ }
+
+ /**
+ * Sets the hotspot BSSID being broadcast by the remote device, or null if hotspot is off.
+ *
+ * @param hotspotBssid The BSSID of the hotspot.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setHotspotBssid(@NonNull String hotspotBssid) {
+ mHotspotBssid = hotspotBssid;
+ return this;
+ }
+
+ /**
+ * Sets the hotspot security types supported by the remote device, or null if hotspot is
+ * off.
+ *
+ * @param hotspotSecurityTypes The array of security types supported by the hotspot.
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setHotspotSecurityTypes(@NonNull @SecurityType int[] hotspotSecurityTypes) {
+ mHotspotSecurityTypes = hotspotSecurityTypes;
+ return this;
+ }
+
+ /**
+ * Builds the {@link TetherNetwork} object.
+ *
+ * @return Returns the built {@link TetherNetwork} object.
+ */
+ @NonNull
+ public TetherNetwork build() {
+ return new TetherNetwork(
+ mDeviceId,
+ mDeviceInfo,
+ mNetworkType,
+ mNetworkName,
+ mHotspotSsid,
+ mHotspotBssid,
+ mHotspotSecurityTypes);
+ }
+ }
+
+ private static void validate(long deviceId, int networkType, String networkName) {
+ if (deviceId < 0) {
+ throw new IllegalArgumentException("DeviceId must be set");
+ }
+ if (networkType != NETWORK_TYPE_CELLULAR && networkType != NETWORK_TYPE_WIFI
+ && networkType != NETWORK_TYPE_ETHERNET && networkType != NETWORK_TYPE_UNKNOWN) {
+ throw new IllegalArgumentException("Illegal network type");
+ }
+ if (Objects.isNull(networkName)) {
+ throw new IllegalArgumentException("NetworkName must be set");
+ }
+ }
+
+ private TetherNetwork(
+ long deviceId,
+ DeviceInfo deviceInfo,
+ @NetworkType int networkType,
+ @NonNull String networkName,
+ @Nullable String hotspotSsid,
+ @Nullable String hotspotBssid,
+ @Nullable @SecurityType int[] hotspotSecurityTypes) {
+ validate(deviceId,
+ networkType,
+ networkName);
+ mDeviceId = deviceId;
+ mDeviceInfo = deviceInfo;
+ mNetworkType = networkType;
+ mNetworkName = networkName;
+ mHotspotSsid = hotspotSsid;
+ mHotspotBssid = hotspotBssid;
+ mHotspotSecurityTypes = hotspotSecurityTypes;
+ }
+
+ /**
+ * Gets the remote device ID.
+ *
+ * @return Returns the locally unique ID for this Instant Tether network.
+ */
+ @NonNull
+ public long getDeviceId() {
+ return mDeviceId;
+ }
+
+ /**
+ * Gets information about the device providing connectivity.
+ *
+ * @return Returns the locally unique ID for this Instant Tether network.
+ */
+ @NonNull
+ public DeviceInfo getDeviceInfo() {
+ return mDeviceInfo;
+ }
+
+ /**
+ * Gets the network type that the remote device is connected to.
+ *
+ * @return Returns the network type as represented by IntDef {@link NetworkType}.
+ */
+ @NonNull
+ @NetworkType
+ public int getNetworkType() {
+ return mNetworkType;
+ }
+
+ /**
+ * Gets the display name of the network the remote device is connected to.
+ *
+ * @return Returns the network display name. (e.g. "Google Fi", "Hotel WiFi", "Ethernet")
+ */
+ @NonNull
+ public String getNetworkName() {
+ return mNetworkName;
+ }
+
+ /**
+ * Gets the hotspot SSID being broadcast by the remote device, or null if hotspot is off.
+ *
+ * @return Returns the SSID of the hotspot. Surrounded by double quotes if UTF-8.
+ */
+ @Nullable
+ public String getHotspotSsid() {
+ return mHotspotSsid;
+ }
+
+ /**
+ * Gets the hotspot BSSID being broadcast by the remote device, or null if hotspot is off.
+ *
+ * @return Returns the BSSID of the hotspot.
+ */
+ @Nullable
+ public String getHotspotBssid() {
+ return mHotspotBssid;
+ }
+
+ /**
+ * Gets the hotspot security types supported by the remote device.
+ *
+ * @return Returns the array of security types supported by the hotspot.
+ */
+ @Nullable
+ @SecurityType
+ public int[] getHotspotSecurityTypes() {
+ return mHotspotSecurityTypes;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TetherNetwork)) return false;
+ TetherNetwork other = (TetherNetwork) obj;
+ return mDeviceId == other.getDeviceId()
+ && Objects.equals(mDeviceInfo, other.getDeviceInfo())
+ && mNetworkType == other.getNetworkType()
+ && Objects.equals(mNetworkName, other.getNetworkName())
+ && Objects.equals(mHotspotSsid, other.getHotspotSsid())
+ && Objects.equals(mHotspotBssid, other.getHotspotBssid())
+ && Arrays.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDeviceId, mDeviceInfo, mNetworkName, mHotspotSsid, mHotspotBssid,
+ Arrays.hashCode(mHotspotSecurityTypes));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mDeviceId);
+ dest.writeTypedObject(mDeviceInfo, 0);
+ dest.writeInt(mNetworkType);
+ dest.writeString(mNetworkName);
+ dest.writeString(mHotspotSsid);
+ dest.writeString(mHotspotBssid);
+ dest.writeIntArray(mHotspotSecurityTypes);
+ }
+
+ @NonNull
+ public static final Creator<TetherNetwork> CREATOR = new Creator<>() {
+ @Override
+ public TetherNetwork createFromParcel(Parcel in) {
+ return new TetherNetwork(in.readLong(), in.readTypedObject(
+ android.net.wifi.sharedconnectivity.app.DeviceInfo.CREATOR),
+ in.readInt(), in.readString(), in.readString(), in.readString(),
+ in.createIntArray());
+ }
+
+ @Override
+ public TetherNetwork[] newArray(int size) {
+ return new TetherNetwork[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return new StringBuilder("TetherNetwork[")
+ .append("deviceId=").append(mDeviceId)
+ .append(", networkType=").append(mNetworkType)
+ .append(", deviceInfo=").append(mDeviceInfo.toString())
+ .append(", networkName=").append(mNetworkName)
+ .append(", hotspotSsid=").append(mHotspotSsid)
+ .append(", hotspotBssid=").append(mHotspotBssid)
+ .append(", hotspotSecurityTypes=").append(Arrays.toString(mHotspotSecurityTypes))
+ .append("]").toString();
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
new file mode 100644
index 0000000..6e56138
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.service;
+
+import android.net.wifi.sharedconnectivity.app.KnownNetwork;
+import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
+import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+
+/*
+ * @hide
+ */
+interface ISharedConnectivityCallback {
+ oneway void onTetherNetworksUpdated(in List<TetherNetwork> networks);
+ oneway void onKnownNetworksUpdated(in List<KnownNetwork> networks);
+ oneway void onSharedConnectivitySettingsChanged(in SharedConnectivitySettingsState state);
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
new file mode 100644
index 0000000..5d79405
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.service;
+
+import android.net.wifi.sharedconnectivity.app.KnownNetwork;
+import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+import android.net.wifi.sharedconnectivity.service.ISharedConnectivityCallback;
+
+/*
+ * @hide
+ */
+interface ISharedConnectivityService {
+ void registerCallback(in ISharedConnectivityCallback callback);
+ void unregisterCallback(in ISharedConnectivityCallback callback);
+ void connectTetherNetwork(in TetherNetwork network);
+ void disconnectTetherNetwork();
+ void connectKnownNetwork(in KnownNetwork network);
+ void forgetKnownNetwork(in KnownNetwork network);
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
new file mode 100644
index 0000000..234319a
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.service;
+
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_SETUP_WIZARD;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.wifi.sharedconnectivity.app.KnownNetwork;
+import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
+import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
+import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * This class is the partly implemented service for injecting Shared Connectivity networks into the
+ * Wi-Fi Pickers and other relevant UI surfaces.
+ *
+ * Implementing application should extend this service and override the indicated methods.
+ * Callers to the service should use {@link SharedConnectivityManager} to bind to the implemented
+ * service as specified in the configuration overlay.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SharedConnectivityService extends Service {
+ private static final String TAG = SharedConnectivityService.class.getSimpleName();
+ private static final boolean DEBUG = true;
+
+ private final Handler mHandler;
+ private final List<ISharedConnectivityCallback> mCallbacks = new ArrayList<>();
+ // Used to find DeathRecipient when unregistering a callback to call unlinkToDeath.
+ private final Map<ISharedConnectivityCallback, DeathRecipient> mDeathRecipientMap =
+ new HashMap<>();
+
+ private List<TetherNetwork> mTetherNetworks = Collections.emptyList();
+ private List<KnownNetwork> mKnownNetworks = Collections.emptyList();
+ private SharedConnectivitySettingsState mSettingsState;
+
+ public SharedConnectivityService() {
+ mHandler = new Handler(getMainLooper());
+ }
+
+ public SharedConnectivityService(@NonNull Handler handler) {
+ mHandler = handler;
+ }
+
+ private final class DeathRecipient implements IBinder.DeathRecipient {
+ ISharedConnectivityCallback mCallback;
+
+ DeathRecipient(ISharedConnectivityCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void binderDied() {
+ mCallbacks.remove(mCallback);
+ mDeathRecipientMap.remove(mCallback);
+ }
+ }
+
+ @Override
+ @Nullable
+ public IBinder onBind(@NonNull Intent intent) {
+ if (DEBUG) Log.i(TAG, "onBind intent=" + intent);
+ return new ISharedConnectivityService.Stub() {
+ @Override
+ public void registerCallback(ISharedConnectivityCallback callback) {
+ checkPermissions();
+ mHandler.post(() -> registerCallback(callback));
+ }
+
+ @Override
+ public void unregisterCallback(ISharedConnectivityCallback callback) {
+ checkPermissions();
+ mHandler.post(() -> unregisterCallback(callback));
+ }
+
+ @Override
+ public void connectTetherNetwork(TetherNetwork network) {
+ checkPermissions();
+ mHandler.post(() -> onConnectTetherNetwork(network));
+ }
+
+ @Override
+ public void disconnectTetherNetwork() {
+ checkPermissions();
+ mHandler.post(() -> onDisconnectTetherNetwork());
+ }
+
+ @Override
+ public void connectKnownNetwork(KnownNetwork network) {
+ checkPermissions();
+ mHandler.post(() -> onConnectKnownNetwork(network));
+ }
+
+ @Override
+ public void forgetKnownNetwork(KnownNetwork network) {
+ checkPermissions();
+ mHandler.post(() -> onForgetKnownNetwork(network));
+ }
+
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ private void checkPermissions() {
+ if (checkCallingPermission(NETWORK_SETTINGS) != PackageManager.PERMISSION_GRANTED
+ && checkCallingPermission(NETWORK_SETUP_WIZARD)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Calling process must have NETWORK_SETTINGS or"
+ + " NETWORK_SETUP_WIZARD permission");
+ }
+ }
+ };
+ }
+
+ private void registerCallback(ISharedConnectivityCallback callback) {
+ // Listener gets triggered on first register using cashed data
+ if (!notifyTetherNetworkUpdate(callback) || !notifyKnownNetworkUpdate(callback)) {
+ if (DEBUG) Log.w(TAG, "Failed to notify client");
+ return;
+ }
+
+ DeathRecipient deathRecipient = new DeathRecipient(callback);
+ try {
+ callback.asBinder().linkToDeath(deathRecipient, 0);
+ mCallbacks.add(callback);
+ mDeathRecipientMap.put(callback, deathRecipient);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in registerCallback", e);
+ }
+ }
+
+ private void unregisterCallback(ISharedConnectivityCallback callback) {
+ DeathRecipient deathRecipient = mDeathRecipientMap.get(callback);
+ if (deathRecipient != null) {
+ callback.asBinder().unlinkToDeath(deathRecipient, 0);
+ mDeathRecipientMap.remove(callback);
+ }
+ mCallbacks.remove(callback);
+ }
+
+ private boolean notifyTetherNetworkUpdate(ISharedConnectivityCallback callback) {
+ try {
+ callback.onTetherNetworksUpdated(mTetherNetworks);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in notifyTetherNetworkUpdate", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean notifyKnownNetworkUpdate(ISharedConnectivityCallback callback) {
+ try {
+ callback.onKnownNetworksUpdated(mKnownNetworks);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in notifyKnownNetworkUpdate", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean notifySettingsStateUpdate(ISharedConnectivityCallback callback) {
+ try {
+ callback.onSharedConnectivitySettingsChanged(mSettingsState);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Exception in notifySettingsStateUpdate", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Implementing application should call this method to provide an up-to-date list of Tether
+ * Networks to be displayed to the user.
+ *
+ * This method updates the cached list and notifies all registered callbacks. Any callbacks that
+ * are inaccessible will be unregistered.
+ *
+ * @param networks The updated list of {@link TetherNetwork} objects.
+ */
+ public final void setTetherNetworks(@NonNull List<TetherNetwork> networks) {
+ mTetherNetworks = networks;
+
+ for (ISharedConnectivityCallback callback:mCallbacks) {
+ notifyTetherNetworkUpdate(callback);
+ }
+ }
+
+ /**
+ * Implementing application should call this method to provide an up-to-date list of Known
+ * Networks to be displayed to the user.
+ *
+ * This method updates the cached list and notifies all registered callbacks. Any callbacks that
+ * are inaccessible will be unregistered.
+ *
+ * @param networks The updated list of {@link KnownNetwork} objects.
+ */
+ public final void setKnownNetworks(@NonNull List<KnownNetwork> networks) {
+ mKnownNetworks = networks;
+
+ for (ISharedConnectivityCallback callback:mCallbacks) {
+ notifyKnownNetworkUpdate(callback);
+ }
+ }
+
+ /**
+ * Implementing application should call this method to provide an up-to-date state of Shared
+ * connectivity settings state.
+ *
+ * This method updates the cached state and notifies all registered callbacks. Any callbacks
+ * that are inaccessible will be unregistered.
+ *
+ * @param settingsState The updated state {@link SharedConnectivitySettingsState}
+ * objects.
+ */
+ public final void setSettingsState(@NonNull SharedConnectivitySettingsState settingsState) {
+ mSettingsState = settingsState;
+
+ for (ISharedConnectivityCallback callback:mCallbacks) {
+ notifySettingsStateUpdate(callback);
+ }
+ }
+
+ /**
+ * Implementing application should implement this method.
+ *
+ * Implementation should initiate a connection to the Tether Network indicated.
+ *
+ * @param network Object identifying the Tether Network the user has requested a connection to.
+ */
+ public abstract void onConnectTetherNetwork(@NonNull TetherNetwork network);
+
+ /**
+ * Implementing application should implement this method.
+ *
+ * Implementation should initiate a disconnection from the active Tether Network.
+ */
+ public abstract void onDisconnectTetherNetwork();
+
+ /**
+ * Implementing application should implement this method.
+ *
+ * Implementation should initiate a connection to the Known Network indicated.
+ *
+ * @param network Object identifying the Known Network the user has requested a connection to.
+ */
+ public abstract void onConnectKnownNetwork(@NonNull KnownNetwork network);
+
+ /**
+ * Implementing application should implement this method.
+ *
+ * Implementation should remove the Known Network indicated from the synced list of networks.
+ *
+ * @param network Object identifying the Known Network the user has requested to forget.
+ */
+ public abstract void onForgetKnownNetwork(@NonNull KnownNetwork network);
+}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
new file mode 100644
index 0000000..f8f0700
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_LAPTOP;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.app.sharedconnectivity.DeviceInfo}.
+ */
+@SmallTest
+public class DeviceInfoTest {
+
+ private static final int DEVICE_TYPE = DEVICE_TYPE_PHONE;
+ private static final String DEVICE_NAME = "TEST_NAME";
+ private static final String DEVICE_MODEL = "TEST_MODEL";
+ private static final int BATTERY_PERCENTAGE = 50;
+ private static final int CONNECTION_STRENGTH = 2;
+
+ private static final int DEVICE_TYPE_1 = DEVICE_TYPE_LAPTOP;
+ private static final String DEVICE_NAME_1 = "TEST_NAME1";
+ private static final String DEVICE_MODEL_1 = "TEST_MODEL1";
+ private static final int BATTERY_PERCENTAGE_1 = 30;
+ private static final int CONNECTION_STRENGTH_1 = 1;
+
+ /**
+ * Verifies parcel serialization/deserialization.
+ */
+ @Test
+ public void testParcelOperation() {
+ DeviceInfo info = buildDeviceInfoBuilder().build();
+
+ Parcel parcelW = Parcel.obtain();
+ info.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ DeviceInfo fromParcel = DeviceInfo.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(info, fromParcel);
+ assertEquals(info.hashCode(), fromParcel.hashCode());
+ }
+
+ /**
+ * Verifies the Equals operation
+ */
+ @Test
+ public void testEqualsOperation() {
+ DeviceInfo info1 = buildDeviceInfoBuilder().build();
+ DeviceInfo info2 = buildDeviceInfoBuilder().build();
+ assertEquals(info1, info2);
+
+ DeviceInfo.Builder builder = buildDeviceInfoBuilder().setDeviceType(DEVICE_TYPE_1);
+ assertNotEquals(info1, builder.build());
+
+ builder = buildDeviceInfoBuilder().setDeviceName(DEVICE_NAME_1);
+ assertNotEquals(info1, builder.build());
+
+ builder = buildDeviceInfoBuilder().setModelName(DEVICE_MODEL_1);
+ assertNotEquals(info1, builder.build());
+
+ builder = buildDeviceInfoBuilder()
+ .setBatteryPercentage(BATTERY_PERCENTAGE_1);
+ assertNotEquals(info1, builder.build());
+
+ builder = buildDeviceInfoBuilder()
+ .setConnectionStrength(CONNECTION_STRENGTH_1);
+ assertNotEquals(info1, builder.build());
+ }
+
+ /**
+ * Verifies the get methods return the expected data.
+ */
+ @Test
+ public void testGetMethods() {
+ DeviceInfo info = buildDeviceInfoBuilder().build();
+ assertEquals(info.getDeviceType(), DEVICE_TYPE);
+ assertEquals(info.getDeviceName(), DEVICE_NAME);
+ assertEquals(info.getModelName(), DEVICE_MODEL);
+ assertEquals(info.getBatteryPercentage(), BATTERY_PERCENTAGE);
+ assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH);
+ assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH);
+ }
+
+ private DeviceInfo.Builder buildDeviceInfoBuilder() {
+ return new DeviceInfo.Builder().setDeviceType(DEVICE_TYPE).setDeviceName(DEVICE_NAME)
+ .setModelName(DEVICE_MODEL).setBatteryPercentage(BATTERY_PERCENTAGE)
+ .setConnectionStrength(CONNECTION_STRENGTH);
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
new file mode 100644
index 0000000..266afcc
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
+import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_CLOUD_SELF;
+import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.app.sharedconnectivity.KnownNetwork}.
+ */
+@SmallTest
+public class KnownNetworkTest {
+
+ private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
+ private static final String SSID = "TEST_SSID";
+ private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
+ private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final int NETWORK_SOURCE_1 = NETWORK_SOURCE_CLOUD_SELF;
+ private static final String SSID_1 = "TEST_SSID1";
+ private static final int[] SECURITY_TYPES_1 = {SECURITY_TYPE_PSK};
+ private static final DeviceInfo DEVICE_INFO_1 = new DeviceInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_PHONE).setDeviceName("TEST_NAME_1")
+ .setModelName("TEST_MODEL_1").setConnectionStrength(3).setBatteryPercentage(33).build();
+
+ /**
+ * Verifies parcel serialization/deserialization.
+ */
+ @Test
+ public void testParcelOperation() {
+ KnownNetwork network = buildKnownNetworkBuilder().build();
+
+ Parcel parcelW = Parcel.obtain();
+ network.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ KnownNetwork fromParcel = KnownNetwork.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(network, fromParcel);
+ assertEquals(network.hashCode(), fromParcel.hashCode());
+ }
+
+ /**
+ * Verifies the Equals operation
+ */
+ @Test
+ public void testEqualsOperation() {
+ KnownNetwork network1 = buildKnownNetworkBuilder().build();
+ KnownNetwork network2 = buildKnownNetworkBuilder().build();
+ assertEquals(network1, network2);
+
+ KnownNetwork.Builder builder = buildKnownNetworkBuilder()
+ .setNetworkSource(NETWORK_SOURCE_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildKnownNetworkBuilder().setSsid(SSID_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildKnownNetworkBuilder().setSecurityTypes(SECURITY_TYPES_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildKnownNetworkBuilder().setDeviceInfo(DEVICE_INFO_1);
+ assertNotEquals(network1, builder.build());
+ }
+
+ /**
+ * Verifies the get methods return the expected data.
+ */
+ @Test
+ public void testGetMethods() {
+ KnownNetwork network = buildKnownNetworkBuilder().build();
+ assertEquals(network.getNetworkSource(), NETWORK_SOURCE);
+ assertEquals(network.getSsid(), SSID);
+ assertArrayEquals(network.getSecurityTypes(), SECURITY_TYPES);
+ assertEquals(network.getDeviceInfo(), DEVICE_INFO);
+ }
+
+ private KnownNetwork.Builder buildKnownNetworkBuilder() {
+ return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID)
+ .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO);
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
new file mode 100644
index 0000000..9aeccac
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
+import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
+import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.sharedconnectivity.service.ISharedConnectivityService;
+import android.os.RemoteException;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for {@link SharedConnectivityManager}.
+ */
+@SmallTest
+public class SharedConnectivityManagerTest {
+ private static final long DEVICE_ID = 11L;
+ private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
+ private static final String NETWORK_NAME = "TEST_NETWORK";
+ private static final String HOTSPOT_SSID = "TEST_SSID";
+ private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
+
+ private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
+ private static final String SSID = "TEST_SSID";
+ private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
+
+ private static final int SERVICE_PACKAGE_ID = 1;
+ private static final int SERVICE_CLASS_ID = 2;
+
+ private static final String SERVICE_PACKAGE_NAME = "TEST_PACKAGE";
+ private static final String SERVICE_CLASS_NAME = "TEST_CLASS";
+ private static final String PACKAGE_NAME = "TEST_PACKAGE";
+
+ @Mock Context mContext;
+ @Mock
+ ISharedConnectivityService mService;
+ @Mock Executor mExecutor;
+ @Mock
+ SharedConnectivityClientCallback mClientCallback;
+ @Mock Resources mResources;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ setResources(mContext);
+ }
+
+ /**
+ * Verifies constructor is binding to service.
+ */
+ @Test
+ public void testBindingToService() {
+ new SharedConnectivityManager(mContext);
+ verify(mContext).bindService(any(), any(), anyInt());
+ }
+
+ /**
+ * Verifies callback is registered in the service only once and only when service is not null.
+ */
+ @Test
+ public void testRegisterCallback() throws Exception {
+ SharedConnectivityManager manager = new SharedConnectivityManager(mContext);
+ manager.setService(null);
+ assertFalse(manager.registerCallback(mExecutor, mClientCallback));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ assertTrue(manager.registerCallback(mExecutor, mClientCallback));
+ verify(mService).registerCallback(any());
+
+ // Registering the same callback twice should fail.
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.registerCallback(mExecutor, mClientCallback);
+ assertFalse(manager.registerCallback(mExecutor, mClientCallback));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).registerCallback(any());
+ assertFalse(manager.registerCallback(mExecutor, mClientCallback));
+ }
+
+ /**
+ * Verifies callback is unregistered from the service if it was registered before and only when
+ * service is not null.
+ */
+ @Test
+ public void testUnregisterCallback() throws Exception {
+ SharedConnectivityManager manager = new SharedConnectivityManager(mContext);
+ manager.setService(null);
+ assertFalse(manager.unregisterCallback(mClientCallback));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.registerCallback(mExecutor, mClientCallback);
+ assertTrue(manager.unregisterCallback(mClientCallback));
+ verify(mService).unregisterCallback(any());
+
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.registerCallback(mExecutor, mClientCallback);
+ manager.unregisterCallback(mClientCallback);
+ assertFalse(manager.unregisterCallback(mClientCallback));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ doThrow(new RemoteException()).when(mService).unregisterCallback(any());
+ assertFalse(manager.unregisterCallback(mClientCallback));
+ }
+
+ /**
+ * Verifies service is called when not null and exceptions are handles when calling
+ * connectTetherNetwork.
+ */
+ @Test
+ public void testConnectTetherNetwork() throws RemoteException {
+ TetherNetwork network = buildTetherNetwork();
+
+ SharedConnectivityManager manager = new SharedConnectivityManager(mContext);
+ manager.setService(null);
+ assertFalse(manager.connectTetherNetwork(network));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.connectTetherNetwork(network);
+ verify(mService).connectTetherNetwork(network);
+
+ doThrow(new RemoteException()).when(mService).connectTetherNetwork(network);
+ assertFalse(manager.connectTetherNetwork(network));
+ }
+
+ /**
+ * Verifies service is called when not null and exceptions are handles when calling
+ * disconnectTetherNetwork.
+ */
+ @Test
+ public void testDisconnectTetherNetwork() throws RemoteException {
+ SharedConnectivityManager manager = new SharedConnectivityManager(mContext);
+ manager.setService(null);
+ assertFalse(manager.disconnectTetherNetwork());
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.disconnectTetherNetwork();
+ verify(mService).disconnectTetherNetwork();
+
+ doThrow(new RemoteException()).when(mService).disconnectTetherNetwork();
+ assertFalse(manager.disconnectTetherNetwork());
+ }
+
+ /**
+ * Verifies service is called when not null and exceptions are handles when calling
+ * connectKnownNetwork.
+ */
+ @Test
+ public void testConnectKnownNetwork() throws RemoteException {
+ KnownNetwork network = buildKnownNetwork();
+
+ SharedConnectivityManager manager = new SharedConnectivityManager(mContext);
+ manager.setService(null);
+ assertFalse(manager.connectKnownNetwork(network));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.connectKnownNetwork(network);
+ verify(mService).connectKnownNetwork(network);
+
+ doThrow(new RemoteException()).when(mService).connectKnownNetwork(network);
+ assertFalse(manager.connectKnownNetwork(network));
+ }
+
+ /**
+ * Verifies service is called when not null and exceptions are handles when calling
+ * forgetKnownNetwork.
+ */
+ @Test
+ public void testForgetKnownNetwork() throws RemoteException {
+ KnownNetwork network = buildKnownNetwork();
+
+ SharedConnectivityManager manager = new SharedConnectivityManager(mContext);
+ manager.setService(null);
+ assertFalse(manager.forgetKnownNetwork(network));
+
+ manager = new SharedConnectivityManager(mContext);
+ manager.setService(mService);
+ manager.forgetKnownNetwork(network);
+ verify(mService).forgetKnownNetwork(network);
+
+ doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network);
+ assertFalse(manager.forgetKnownNetwork(network));
+ }
+
+ private void setResources(@Mock Context context) {
+ when(context.getResources()).thenReturn(mResources);
+ when(context.getPackageName()).thenReturn(PACKAGE_NAME);
+ when(mResources.getIdentifier(anyString(), anyString(), anyString()))
+ .thenReturn(SERVICE_PACKAGE_ID, SERVICE_CLASS_ID);
+ when(mResources.getString(SERVICE_PACKAGE_ID)).thenReturn(SERVICE_PACKAGE_NAME);
+ when(mResources.getString(SERVICE_CLASS_ID)).thenReturn(SERVICE_CLASS_NAME);
+ }
+
+ private TetherNetwork buildTetherNetwork() {
+ return new TetherNetwork.Builder()
+ .setDeviceId(DEVICE_ID)
+ .setDeviceInfo(DEVICE_INFO)
+ .setNetworkType(NETWORK_TYPE)
+ .setNetworkName(NETWORK_NAME)
+ .setHotspotSsid(HOTSPOT_SSID)
+ .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES)
+ .build();
+ }
+
+ private KnownNetwork buildKnownNetwork() {
+ return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID)
+ .setSecurityTypes(SECURITY_TYPES).build();
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
new file mode 100644
index 0000000..3137c72
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState}.
+ */
+@SmallTest
+public class SharedConnectivitySettingsStateTest {
+ private static final boolean INSTANT_TETHER_STATE = true;
+
+ private static final boolean INSTANT_TETHER_STATE_1 = false;
+ /**
+ * Verifies parcel serialization/deserialization.
+ */
+ @Test
+ public void testParcelOperation() {
+ SharedConnectivitySettingsState state = buildSettingsStateBuilder().build();
+
+ Parcel parcelW = Parcel.obtain();
+ state.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ SharedConnectivitySettingsState fromParcel =
+ SharedConnectivitySettingsState.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(state, fromParcel);
+ assertEquals(state.hashCode(), fromParcel.hashCode());
+ }
+
+ /**
+ * Verifies the Equals operation
+ */
+ @Test
+ public void testEqualsOperation() {
+ SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build();
+ SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build();
+ assertEquals(state1, state2);
+
+ SharedConnectivitySettingsState.Builder builder = buildSettingsStateBuilder()
+ .setInstantTetherEnabled(INSTANT_TETHER_STATE_1);
+ assertNotEquals(state1, builder.build());
+ }
+
+ /**
+ * Verifies the get methods return the expected data.
+ */
+ @Test
+ public void testGetMethods() {
+ SharedConnectivitySettingsState state = buildSettingsStateBuilder().build();
+ assertEquals(state.isInstantTetherEnabled(), INSTANT_TETHER_STATE);
+ }
+
+ private SharedConnectivitySettingsState.Builder buildSettingsStateBuilder() {
+ return new SharedConnectivitySettingsState.Builder()
+ .setInstantTetherEnabled(INSTANT_TETHER_STATE);
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
new file mode 100644
index 0000000..b01aec4
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.app;
+
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
+import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
+import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
+import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
+import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_WIFI;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetwork}.
+ */
+@SmallTest
+public class TetherNetworkTest {
+ private static final long DEVICE_ID = 11L;
+ private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
+ private static final String NETWORK_NAME = "TEST_NETWORK";
+ private static final String HOTSPOT_SSID = "TEST_SSID";
+ private static final String HOTSPOT_BSSID = "TEST _BSSID";
+ private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
+
+ private static final long DEVICE_ID_1 = 111L;
+ private static final DeviceInfo DEVICE_INFO_1 = new DeviceInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_PHONE).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final int NETWORK_TYPE_1 = NETWORK_TYPE_WIFI;
+ private static final String NETWORK_NAME_1 = "TEST_NETWORK1";
+ private static final String HOTSPOT_SSID_1 = "TEST_SSID1";
+ private static final String HOTSPOT_BSSID_1 = "TEST _BSSID1";
+ private static final int[] HOTSPOT_SECURITY_TYPES_1 = {SECURITY_TYPE_PSK, SECURITY_TYPE_EAP};
+
+ /**
+ * Verifies parcel serialization/deserialization.
+ */
+ @Test
+ public void testParcelOperation() {
+ TetherNetwork network = buildTetherNetworkBuilder().build();
+
+ Parcel parcelW = Parcel.obtain();
+ network.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ TetherNetwork fromParcel = TetherNetwork.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(network, fromParcel);
+ assertEquals(network.hashCode(), fromParcel.hashCode());
+ }
+
+ /**
+ * Verifies the Equals operation
+ */
+ @Test
+ public void testEqualsOperation() {
+ TetherNetwork network1 = buildTetherNetworkBuilder().build();
+ TetherNetwork network2 = buildTetherNetworkBuilder().build();
+ assertEquals(network1, network2);
+
+ TetherNetwork.Builder builder = buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildTetherNetworkBuilder().setDeviceInfo(DEVICE_INFO_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildTetherNetworkBuilder().setNetworkType(NETWORK_TYPE_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildTetherNetworkBuilder().setNetworkName(NETWORK_NAME_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildTetherNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildTetherNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
+ assertNotEquals(network1, builder.build());
+
+ builder = buildTetherNetworkBuilder().setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES_1);
+ assertNotEquals(network1, builder.build());
+ }
+
+ /**
+ * Verifies the get methods return the expected data.
+ */
+ @Test
+ public void testGetMethods() {
+ TetherNetwork network = buildTetherNetworkBuilder().build();
+ assertEquals(network.getDeviceId(), DEVICE_ID);
+ assertEquals(network.getDeviceInfo(), DEVICE_INFO);
+ assertEquals(network.getNetworkType(), NETWORK_TYPE);
+ assertEquals(network.getNetworkName(), NETWORK_NAME);
+ assertEquals(network.getHotspotSsid(), HOTSPOT_SSID);
+ assertEquals(network.getHotspotBssid(), HOTSPOT_BSSID);
+ assertArrayEquals(network.getHotspotSecurityTypes(), HOTSPOT_SECURITY_TYPES);
+ }
+
+ private TetherNetwork.Builder buildTetherNetworkBuilder() {
+ return new TetherNetwork.Builder()
+ .setDeviceId(DEVICE_ID)
+ .setDeviceInfo(DEVICE_INFO)
+ .setNetworkType(NETWORK_TYPE)
+ .setNetworkName(NETWORK_NAME)
+ .setHotspotSsid(HOTSPOT_SSID)
+ .setHotspotBssid(HOTSPOT_BSSID)
+ .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES);
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
new file mode 100644
index 0000000..e15be8b
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.sharedconnectivity.service;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Intent;
+import android.net.wifi.sharedconnectivity.app.KnownNetwork;
+import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+import android.os.Handler;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.sharedconnectivity.service.SharedConnectivityService}.
+ */
+@SmallTest
+public class SharedConnectivityServiceTest {
+
+ /**
+ * Verifies service returns
+ */
+ @Test
+ public void testOnBind() {
+ SharedConnectivityService service = createService();
+ assertNotNull(service.onBind(new Intent()));
+ }
+
+ @Test
+ public void testCallbacks() {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+ }
+
+ private SharedConnectivityService createService() {
+ return new SharedConnectivityService(new Handler(new TestLooper().getLooper())) {
+ @Override
+ public void onConnectTetherNetwork(TetherNetwork network) {}
+
+ @Override
+ public void onDisconnectTetherNetwork() {}
+
+ @Override
+ public void onConnectKnownNetwork(KnownNetwork network) {}
+
+ @Override
+ public void onForgetKnownNetwork(KnownNetwork network) {}
+ };
+ }
+}