Define stub and data classes for SharedConnectivity Service

Test: atest SharedConnectivityServiceTest
Bug: 257348873
API-Coverage-Bug: 265968015
Change-Id: Idcf42036c73be9dd51416410e6b15ef4c1bb2af7
This commit is contained in:
Isaac Katzenelson
2023-01-27 22:16:27 +00:00
parent ccd98b966e
commit 86e2e4f00f
22 changed files with 2762 additions and 1 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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"],
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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) {}
};
}
}