diff options
author | 2023-01-27 22:16:27 +0000 | |
---|---|---|
committer | 2023-02-03 17:35:28 +0000 | |
commit | 86e2e4f00f0271443c1a994bf48ae1eaa234acb8 (patch) | |
tree | 867e9a8541fff60ccdbdf8c52dc101a032d97ef4 | |
parent | ccd98b966ec9a393958b5e6e842cfbdbceef01d1 (diff) |
Define stub and data classes for SharedConnectivity Service
Test: atest SharedConnectivityServiceTest
Bug: 257348873
API-Coverage-Bug: 265968015
Change-Id: Idcf42036c73be9dd51416410e6b15ef4c1bb2af7
22 files changed, 2762 insertions, 1 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0476d7916a38..2ad9a9bbabb3 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9782,6 +9782,133 @@ package android.net.wifi.nl80211 { } +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 360113b73131..b4b5323ec512 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1866,6 +1866,14 @@ package android.net { } +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 225e750923fd..434226d75b28 100644 --- a/wifi/java/Android.bp +++ b/wifi/java/Android.bp @@ -27,7 +27,10 @@ package { 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 000000000000..35d5c15a161b --- /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 000000000000..7874b2a22e02 --- /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 000000000000..140d72ace70d --- /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 000000000000..34b7e94e0fda --- /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 000000000000..dcb5201b4594 --- /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 000000000000..b43e4f7f57b5 --- /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 000000000000..289afacb9aa0 --- /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 000000000000..dd2fa94ccf2d --- /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 000000000000..6cc4cfe7dce5 --- /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 000000000000..bbdad5344646 --- /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 000000000000..6e5613886cdc --- /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 000000000000..5d79405ba19a --- /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 000000000000..234319ad5318 --- /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 000000000000..f8f07008e34b --- /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 000000000000..266afcc9a1a6 --- /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 000000000000..9aeccac1968e --- /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 000000000000..3137c7268ae0 --- /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 000000000000..b01aec4ad1c1 --- /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 000000000000..e15be8b24a67 --- /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) {} + }; + } +} |