diff options
Diffstat (limited to 'wifi/java')
14 files changed, 1847 insertions, 1 deletions
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); +} |