diff options
26 files changed, 3274 insertions, 1 deletions
diff --git a/Android.mk b/Android.mk index 0a7516577ea6..70eda598a165 100644 --- a/Android.mk +++ b/Android.mk @@ -410,6 +410,9 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \ + wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \ + wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \ + wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ wifi/java/android/net/wifi/IWifiScanner.aidl \ wifi/java/android/net/wifi/IRttManager.aidl \ @@ -476,6 +479,11 @@ aidl_files := \ frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \ frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \ frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \ + frameworks/base/wifi/java/android/net/wifi/nan/ConfigRequest.aidl \ + frameworks/base/wifi/java/android/net/wifi/nan/PublishData.aidl \ + frameworks/base/wifi/java/android/net/wifi/nan/SubscribeData.aidl \ + frameworks/base/wifi/java/android/net/wifi/nan/PublishSettings.aidl \ + frameworks/base/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl \ frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \ frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \ frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \ diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3d264c6f2cba..3eb1b9124b54 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -78,6 +78,8 @@ import android.net.wifi.IWifiScanner; import android.net.wifi.RttManager; import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner; +import android.net.wifi.nan.IWifiNanManager; +import android.net.wifi.nan.WifiNanManager; import android.net.wifi.p2p.IWifiP2pManager; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.passpoint.IWifiPasspointManager; @@ -499,6 +501,18 @@ final class SystemServiceRegistry { return new WifiP2pManager(service); }}); + registerService(Context.WIFI_NAN_SERVICE, WifiNanManager.class, + new StaticServiceFetcher<WifiNanManager>() { + @Override + public WifiNanManager createService() { + IBinder b = ServiceManager.getService(Context.WIFI_NAN_SERVICE); + IWifiNanManager service = IWifiNanManager.Stub.asInterface(b); + if (service == null) { + return null; + } + return new WifiNanManager(service); + }}); + registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class, new CachedServiceFetcher<WifiScanner>() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 758b6fffb1a7..43d4ff2c0f9e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2460,6 +2460,7 @@ public abstract class Context { NETWORK_STATS_SERVICE, //@hide: NETWORK_POLICY_SERVICE, WIFI_SERVICE, + WIFI_NAN_SERVICE, WIFI_PASSPOINT_SERVICE, WIFI_P2P_SERVICE, WIFI_SCANNING_SERVICE, @@ -2922,6 +2923,17 @@ public abstract class Context { public static final String WIFI_P2P_SERVICE = "wifip2p"; /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.net.wifi.nan.WifiNanManager} for handling management of + * Wi-Fi NAN discovery and connections. + * + * @see #getSystemService + * @see android.net.wifi.nan.WifiNanManager + * @hide PROPOSED_NAN_API + */ + public static final String WIFI_NAN_SERVICE = "wifinan"; + + /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.wifi.WifiScanner} for scanning the wifi universe * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c8e9402e6442..e674e62b92d7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1643,6 +1643,16 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Wi-Fi Aware (NAN) + * networking. + * + * @hide PROPOSED_NAN_API + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_WIFI_NAN = "android.hardware.wifi.nan"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: This is a device dedicated to showing UI * on a vehicle headunit. A headunit here is defined to be inside a * vehicle that may or may not be moving. A headunit uses either a diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0619d76e2e03..81c5a1ac55d5 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -130,6 +130,8 @@ public final class SystemServer { "com.android.server.midi.MidiService$Lifecycle"; private static final String WIFI_SERVICE_CLASS = "com.android.server.wifi.WifiService"; + private static final String WIFI_NAN_SERVICE_CLASS = + "com.android.server.wifi.nan.WifiNanService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; private static final String ETHERNET_SERVICE_CLASS = @@ -727,6 +729,11 @@ public final class SystemServer { } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)) { + mSystemServiceManager.startService(WIFI_NAN_SERVICE_CLASS); + } else { + Slog.i(TAG, "No Wi-Fi NAN Service (NAN support Not Present)"); + } mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); mSystemServiceManager.startService(WIFI_SERVICE_CLASS); mSystemServiceManager.startService( diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index bd030e8e5f32..50f2cf0a48f7 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -999,7 +999,7 @@ public class WifiManager { /** * @return true if this adapter supports Neighbour Awareness Network APIs - * @hide + * @hide PROPOSED_NAN_API */ public boolean isNanSupported() { return isFeatureSupported(WIFI_FEATURE_NAN); diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.aidl b/wifi/java/android/net/wifi/nan/ConfigRequest.aidl new file mode 100644 index 000000000000..38dddc239247 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/ConfigRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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.nan; + +parcelable ConfigRequest; diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/nan/ConfigRequest.java new file mode 100644 index 000000000000..23e37547b9f0 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/ConfigRequest.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Defines a request object to configure a Wi-Fi NAN network. Built using + * {@link ConfigRequest.Builder}. Configuration is requested using + * {@link WifiNanManager#requestConfig(ConfigRequest)}. Note that the actual + * achieved configuration may be different from the requested configuration - + * since multiple applications may request different configurations. + * + * @hide PROPOSED_NAN_API + */ +public class ConfigRequest implements Parcelable { + /** + * Lower range of possible cluster ID. + * + * @hide + */ + public static final int CLUSTER_ID_MIN = 0; + + /** + * Upper range of possible cluster ID. + * + * @hide + */ + public static final int CLUSTER_ID_MAX = 0xFFFF; + + /** + * Indicates whether 5G band support is requested. + * + * @hide + */ + public final boolean mSupport5gBand; + + /** + * Specifies the desired master preference. + * + * @hide + */ + public final int mMasterPreference; + + /** + * Specifies the desired lower range of the cluster ID. Must be lower then + * {@link ConfigRequest#mClusterHigh}. + * + * @hide + */ + public final int mClusterLow; + + /** + * Specifies the desired higher range of the cluster ID. Must be higher then + * {@link ConfigRequest#mClusterLow}. + * + * @hide + */ + public final int mClusterHigh; + + private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow, + int clusterHigh) { + mSupport5gBand = support5gBand; + mMasterPreference = masterPreference; + mClusterLow = clusterLow; + mClusterHigh = clusterHigh; + } + + @Override + public String toString() { + return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference=" + + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh=" + + mClusterHigh + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSupport5gBand ? 1 : 0); + dest.writeInt(mMasterPreference); + dest.writeInt(mClusterLow); + dest.writeInt(mClusterHigh); + } + + public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() { + @Override + public ConfigRequest[] newArray(int size) { + return new ConfigRequest[size]; + } + + @Override + public ConfigRequest createFromParcel(Parcel in) { + boolean support5gBand = in.readInt() != 0; + int masterPreference = in.readInt(); + int clusterLow = in.readInt(); + int clusterHigh = in.readInt(); + return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh); + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ConfigRequest)) { + return false; + } + + ConfigRequest lhs = (ConfigRequest) o; + + return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference + && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh; + } + + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + (mSupport5gBand ? 1 : 0); + result = 31 * result + mMasterPreference; + result = 31 * result + mClusterLow; + result = 31 * result + mClusterHigh; + + return result; + } + + /** + * Builder used to build {@link ConfigRequest} objects. + */ + public static final class Builder { + private boolean mSupport5gBand; + private int mMasterPreference; + private int mClusterLow; + private int mClusterHigh; + + /** + * Default constructor for the Builder. + */ + public Builder() { + mSupport5gBand = false; + mMasterPreference = 0; + mClusterLow = 0; + mClusterHigh = CLUSTER_ID_MAX; + } + + /** + * Specify whether 5G band support is required in this request. + * + * @param support5gBand Support for 5G band is required. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setSupport5gBand(boolean support5gBand) { + mSupport5gBand = support5gBand; + return this; + } + + /** + * Specify the Master Preference requested. The permitted range is 0 to + * 255 with 1 and 255 excluded (reserved). + * + * @param masterPreference The requested master preference + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setMasterPreference(int masterPreference) { + if (masterPreference < 0) { + throw new IllegalArgumentException( + "Master Preference specification must be non-negative"); + } + if (masterPreference == 1 || masterPreference == 255 || masterPreference > 255) { + throw new IllegalArgumentException("Master Preference specification must not " + + "exceed 255 or use 1 or 255 (reserved values)"); + } + + mMasterPreference = masterPreference; + return this; + } + + /** + * The Cluster ID is generated randomly for new NAN networks. Specify + * the lower range of the cluster ID. The upper range is specified using + * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted + * range is 0 to the value specified by + * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality is + * permitted which restricts the Cluster ID to the specified value. + * + * @param clusterLow The lower range of the generated cluster ID. + * @return The builder to facilitate chaining + * {@code builder.setClusterLow(..).setClusterHigh(..)}. + */ + public Builder setClusterLow(int clusterLow) { + if (clusterLow < CLUSTER_ID_MIN) { + throw new IllegalArgumentException("Cluster specification must be non-negative"); + } + if (clusterLow > CLUSTER_ID_MAX) { + throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF"); + } + + mClusterLow = clusterLow; + return this; + } + + /** + * The Cluster ID is generated randomly for new NAN networks. Specify + * the lower upper of the cluster ID. The lower range is specified using + * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted + * range is the value specified by + * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF. Equality + * is permitted which restricts the Cluster ID to the specified value. + * + * @param clusterHigh The upper range of the generated cluster ID. + * @return The builder to facilitate chaining + * {@code builder.setClusterLow(..).setClusterHigh(..)}. + */ + public Builder setClusterHigh(int clusterHigh) { + if (clusterHigh < CLUSTER_ID_MIN) { + throw new IllegalArgumentException("Cluster specification must be non-negative"); + } + if (clusterHigh > CLUSTER_ID_MAX) { + throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF"); + } + + mClusterHigh = clusterHigh; + return this; + } + + /** + * Build {@link ConfigRequest} given the current requests made on the + * builder. + */ + public ConfigRequest build() { + if (mClusterLow > mClusterHigh) { + throw new IllegalArgumentException( + "Invalid argument combination - must have Cluster Low <= Cluster High"); + } + + return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl new file mode 100644 index 000000000000..13efc361fbb3 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2016, 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.nan; + +import android.net.wifi.nan.ConfigRequest; + +/** + * Callback interface that WifiNanManager implements + * + * {@hide} + */ +oneway interface IWifiNanEventListener +{ + void onConfigCompleted(in ConfigRequest completedConfig); + void onConfigFailed(int reason); + void onNanDown(int reason); + void onIdentityChanged(); +} diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl new file mode 100644 index 000000000000..ff3d29f41545 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2016, 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.nan; + +import android.app.PendingIntent; + +import android.net.wifi.nan.ConfigRequest; +import android.net.wifi.nan.IWifiNanEventListener; +import android.net.wifi.nan.IWifiNanSessionListener; +import android.net.wifi.nan.PublishData; +import android.net.wifi.nan.PublishSettings; +import android.net.wifi.nan.SubscribeData; +import android.net.wifi.nan.SubscribeSettings; + +/** + * Interface that WifiNanService implements + * + * {@hide} + */ +interface IWifiNanManager +{ + // client API + void connect(in IBinder binder, in IWifiNanEventListener listener, int events); + void disconnect(in IBinder binder); + void requestConfig(in ConfigRequest configRequest); + + // session API + int createSession(in IWifiNanSessionListener listener, int events); + void publish(int sessionId, in PublishData publishData, in PublishSettings publishSettings); + void subscribe(int sessionId, in SubscribeData subscribeData, + in SubscribeSettings subscribeSettings); + void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength); + void stopSession(int sessionId); + void destroySession(int sessionId); +} diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl new file mode 100644 index 000000000000..773f83bc70dc --- /dev/null +++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2016, 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.nan; + +/** + * Callback interface that WifiNanManager implements + * + * {@hide} + */ +oneway interface IWifiNanSessionListener +{ + void onPublishFail(int reason); + void onPublishTerminated(int reason); + + void onSubscribeFail(int reason); + void onSubscribeTerminated(int reason); + + void onMatch(int peerId, in byte[] serviceSpecificInfo, + int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength); + + void onMessageSendSuccess(); + void onMessageSendFail(int reason); + void onMessageReceived(int peerId, in byte[] message, int messageLength); +} diff --git a/wifi/java/android/net/wifi/nan/PublishData.aidl b/wifi/java/android/net/wifi/nan/PublishData.aidl new file mode 100644 index 000000000000..15e4ddfd1c0b --- /dev/null +++ b/wifi/java/android/net/wifi/nan/PublishData.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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.nan; + +parcelable PublishData; diff --git a/wifi/java/android/net/wifi/nan/PublishData.java b/wifi/java/android/net/wifi/nan/PublishData.java new file mode 100644 index 000000000000..80119eb0f4fd --- /dev/null +++ b/wifi/java/android/net/wifi/nan/PublishData.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * Defines the data for a NAN publish session. Built using + * {@link PublishData.Builder}. Publish is done using + * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)} + * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}. + * @hide PROPOSED_NAN_API + */ +public class PublishData implements Parcelable { + /** + * @hide + */ + public final String mServiceName; + + /** + * @hide + */ + public final int mServiceSpecificInfoLength; + + /** + * @hide + */ + public final byte[] mServiceSpecificInfo; + + /** + * @hide + */ + public final int mTxFilterLength; + + /** + * @hide + */ + public final byte[] mTxFilter; + + /** + * @hide + */ + public final int mRxFilterLength; + + /** + * @hide + */ + public final byte[] mRxFilter; + + private PublishData(String serviceName, byte[] serviceSpecificInfo, + int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter, + int rxFilterLength) { + mServiceName = serviceName; + mServiceSpecificInfoLength = serviceSpecificInfoLength; + mServiceSpecificInfo = serviceSpecificInfo; + mTxFilterLength = txFilterLength; + mTxFilter = txFilter; + mRxFilterLength = rxFilterLength; + mRxFilter = rxFilter; + } + + @Override + public String toString() { + return "PublishData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='" + + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength)) + + "', mTxFilter=" + + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString() + + ", mRxFilter=" + + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString() + + "']"; + } + + @Override + public int describeContents() { + return 0; + } + + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mServiceName); + dest.writeInt(mServiceSpecificInfoLength); + if (mServiceSpecificInfoLength != 0) { + dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength); + } + dest.writeInt(mTxFilterLength); + if (mTxFilterLength != 0) { + dest.writeByteArray(mTxFilter, 0, mTxFilterLength); + } + dest.writeInt(mRxFilterLength); + if (mRxFilterLength != 0) { + dest.writeByteArray(mRxFilter, 0, mRxFilterLength); + } + } + + public static final Creator<PublishData> CREATOR = new Creator<PublishData>() { + @Override + public PublishData[] newArray(int size) { + return new PublishData[size]; + } + + @Override + public PublishData createFromParcel(Parcel in) { + String serviceName = in.readString(); + int ssiLength = in.readInt(); + byte[] ssi = new byte[ssiLength]; + if (ssiLength != 0) { + in.readByteArray(ssi); + } + int txFilterLength = in.readInt(); + byte[] txFilter = new byte[txFilterLength]; + if (txFilterLength != 0) { + in.readByteArray(txFilter); + } + int rxFilterLength = in.readInt(); + byte[] rxFilter = new byte[rxFilterLength]; + if (rxFilterLength != 0) { + in.readByteArray(rxFilter); + } + + return new PublishData(serviceName, ssi, ssiLength, txFilter, txFilterLength, rxFilter, + rxFilterLength); + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof PublishData)) { + return false; + } + + PublishData lhs = (PublishData) o; + + if (!mServiceName.equals(lhs.mServiceName) + || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength + || mTxFilterLength != lhs.mTxFilterLength + || mRxFilterLength != lhs.mRxFilterLength) { + return false; + } + + if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) { + for (int i = 0; i < mServiceSpecificInfoLength; ++i) { + if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) { + return false; + } + } + } else if (mServiceSpecificInfoLength != 0) { + return false; // invalid != invalid + } + + if (mTxFilter != null && lhs.mTxFilter != null) { + for (int i = 0; i < mTxFilterLength; ++i) { + if (mTxFilter[i] != lhs.mTxFilter[i]) { + return false; + } + } + } else if (mTxFilterLength != 0) { + return false; // invalid != invalid + } + + if (mRxFilter != null && lhs.mRxFilter != null) { + for (int i = 0; i < mRxFilterLength; ++i) { + if (mRxFilter[i] != lhs.mRxFilter[i]) { + return false; + } + } + } else if (mRxFilterLength != 0) { + return false; // invalid != invalid + } + + return true; + } + + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + mServiceName.hashCode(); + result = 31 * result + mServiceSpecificInfoLength; + result = 31 * result + Arrays.hashCode(mServiceSpecificInfo); + result = 31 * result + mTxFilterLength; + result = 31 * result + Arrays.hashCode(mTxFilter); + result = 31 * result + mRxFilterLength; + result = 31 * result + Arrays.hashCode(mRxFilter); + + return result; + } + + /** + * Builder used to build {@link PublishData} objects. + */ + public static final class Builder { + private String mServiceName; + private int mServiceSpecificInfoLength; + private byte[] mServiceSpecificInfo = new byte[0]; + private int mTxFilterLength; + private byte[] mTxFilter = new byte[0]; + private int mRxFilterLength; + private byte[] mRxFilter = new byte[0]; + + /** + * Specify the service name of the publish session. The actual on-air + * value is a 6 byte hashed representation of this string. + * + * @param serviceName The service name for the publish session. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setServiceName(String serviceName) { + mServiceName = serviceName; + return this; + } + + /** + * Specify service specific information for the publish session. This is + * a free-form byte array available to the application to send + * additional information as part of the discovery operation - i.e. it + * will not be used to determine whether a publish/subscribe match + * occurs. + * + * @param serviceSpecificInfo A byte-array for the service-specific + * information field. + * @param serviceSpecificInfoLength The length of the byte-array to be + * used. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo, + int serviceSpecificInfoLength) { + if (serviceSpecificInfoLength != 0 && (serviceSpecificInfo == null + || serviceSpecificInfo.length < serviceSpecificInfoLength)) { + throw new IllegalArgumentException("Non-matching combination of " + + "serviceSpecificInfo and serviceSpecificInfoLength"); + } + mServiceSpecificInfoLength = serviceSpecificInfoLength; + mServiceSpecificInfo = serviceSpecificInfo; + return this; + } + + /** + * Specify service specific information for the publish session - same + * as {@link PublishData.Builder#setServiceSpecificInfo(byte[], int)} + * but obtaining the data from a String. + * + * @param serviceSpecificInfoStr The service specific information string + * to be included (as a byte array) in the publish + * information. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) { + mServiceSpecificInfoLength = serviceSpecificInfoStr.length(); + mServiceSpecificInfo = serviceSpecificInfoStr.getBytes(); + return this; + } + + /** + * The transmit filter for an active publish session + * {@link PublishSettings.Builder#setPublishType(int)} and + * {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}. Included in + * transmitted publish packets and used by receivers (subscribers) to + * determine whether they match - in addition to just relying on the + * service name. + * <p> + * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * is available to form and parse. + * + * @param txFilter The byte-array containing the LV formatted transmit + * filter. + * @param txFilterLength The number of bytes in the transmit filter + * argument. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setTxFilter(byte[] txFilter, int txFilterLength) { + if (txFilterLength != 0 && (txFilter == null || txFilter.length < txFilterLength)) { + throw new IllegalArgumentException( + "Non-matching combination of txFilter and txFilterLength"); + } + mTxFilter = txFilter; + mTxFilterLength = txFilterLength; + return this; + } + + /** + * The transmit filter for a passive publish session + * {@link PublishSettings.Builder#setPublishType(int)} and + * {@link PublishSettings#PUBLISH_TYPE_SOLICITED}. Used by the publisher + * to determine whether they match transmitted subscriber packets + * (active subscribers) - in addition to just relying on the service + * name. + * <p> + * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * is available to form and parse. + * + * @param rxFilter The byte-array containing the LV formatted receive + * filter. + * @param rxFilterLength The number of bytes in the receive filter + * argument. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) { + if (rxFilterLength != 0 && (rxFilter == null || rxFilter.length < rxFilterLength)) { + throw new IllegalArgumentException( + "Non-matching combination of rxFilter and rxFilterLength"); + } + mRxFilter = rxFilter; + mRxFilterLength = rxFilterLength; + return this; + } + + /** + * Build {@link PublishData} given the current requests made on the + * builder. + */ + public PublishData build() { + return new PublishData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength, + mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.aidl b/wifi/java/android/net/wifi/nan/PublishSettings.aidl new file mode 100644 index 000000000000..ff692936bf06 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/PublishSettings.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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.nan; + +parcelable PublishSettings; diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.java b/wifi/java/android/net/wifi/nan/PublishSettings.java new file mode 100644 index 000000000000..bbc53408f2b5 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/PublishSettings.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Defines the settings (configuration) for a NAN publish session. Built using + * {@link PublishSettings.Builder}. Publish is done using + * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)} + * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}. + * + * @hide PROPOSED_NAN_API + */ +public class PublishSettings implements Parcelable { + + /** + * Defines an unsolicited publish session - i.e. a publish session where + * publish packets are transmitted over-the-air. Configuration is done using + * {@link PublishSettings.Builder#setPublishType(int)}. + */ + public static final int PUBLISH_TYPE_UNSOLICITED = 0; + + /** + * Defines a solicited publish session - i.e. a publish session where + * publish packets are not transmitted over-the-air and the device listens + * and matches to transmitted subscribe packets. Configuration is done using + * {@link PublishSettings.Builder#setPublishType(int)}. + */ + public static final int PUBLISH_TYPE_SOLICITED = 1; + + /** + * @hide + */ + public final int mPublishType; + + /** + * @hide + */ + public final int mPublishCount; + + /** + * @hide + */ + public final int mTtlSec; + + private PublishSettings(int publishType, int publichCount, int ttlSec) { + mPublishType = publishType; + mPublishCount = publichCount; + mTtlSec = ttlSec; + } + + @Override + public String toString() { + return "PublishSettings [mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount + + ", mTtlSec=" + mTtlSec + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPublishType); + dest.writeInt(mPublishCount); + dest.writeInt(mTtlSec); + } + + public static final Creator<PublishSettings> CREATOR = new Creator<PublishSettings>() { + @Override + public PublishSettings[] newArray(int size) { + return new PublishSettings[size]; + } + + @Override + public PublishSettings createFromParcel(Parcel in) { + int publishType = in.readInt(); + int publishCount = in.readInt(); + int ttlSec = in.readInt(); + return new PublishSettings(publishType, publishCount, ttlSec); + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof PublishSettings)) { + return false; + } + + PublishSettings lhs = (PublishSettings) o; + + return mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount + && mTtlSec == lhs.mTtlSec; + } + + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + mPublishType; + result = 31 * result + mPublishCount; + result = 31 * result + mTtlSec; + + return result; + } + + /** + * Builder used to build {@link PublishSettings} objects. + */ + public static final class Builder { + int mPublishType; + int mPublishCount; + int mTtlSec; + + /** + * Sets the type of the publish session: solicited (aka active - publish + * packets are transmitted over-the-air), or unsolicited (aka passive - + * no publish packets are transmitted, a match is made against an active + * subscribe session whose packets are transmitted over-the-air). + * + * @param publishType Publish session type: solicited ( + * {@link PublishSettings#PUBLISH_TYPE_SOLICITED}) or + * unsolicited ( + * {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}). + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setPublishType(int publishType) { + if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) { + throw new IllegalArgumentException("Invalid publishType - " + publishType); + } + mPublishType = publishType; + return this; + } + + /** + * Sets the number of times a solicited ( + * {@link PublishSettings.Builder#setPublishType(int)}) publish session + * will transmit a packet. When the count is reached an event will be + * generated for {@link WifiNanSessionListener#onPublishTerminated(int)} + * with reason={@link WifiNanSessionListener#TERMINATE_REASON_DONE}. + * + * @param publishCount Number of publish packets to transmit. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setPublishCount(int publishCount) { + if (publishCount < 0) { + throw new IllegalArgumentException("Invalid publishCount - must be non-negative"); + } + mPublishCount = publishCount; + return this; + } + + /** + * Sets the time interval (in seconds) a solicited ( + * {@link PublishSettings.Builder#setPublishCount(int)}) publish session + * will be alive - i.e. transmitting a packet. When the TTL is reached + * an event will be generated for + * {@link WifiNanSessionListener#onPublishTerminated(int)} with reason= + * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}. + * + * @param ttlSec Lifetime of a publish session in seconds. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setTtlSec(int ttlSec) { + if (ttlSec < 0) { + throw new IllegalArgumentException("Invalid ttlSec - must be non-negative"); + } + mTtlSec = ttlSec; + return this; + } + + /** + * Build {@link PublishSettings} given the current requests made on the + * builder. + */ + public PublishSettings build() { + return new PublishSettings(mPublishType, mPublishCount, mTtlSec); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.aidl b/wifi/java/android/net/wifi/nan/SubscribeData.aidl new file mode 100644 index 000000000000..662fdb83f74c --- /dev/null +++ b/wifi/java/android/net/wifi/nan/SubscribeData.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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.nan; + +parcelable SubscribeData; diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.java b/wifi/java/android/net/wifi/nan/SubscribeData.java new file mode 100644 index 000000000000..cd6e91882834 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/SubscribeData.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * Defines the data for a NAN subscribe session. Built using + * {@link SubscribeData.Builder}. Subscribe is done using + * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)} + * or + * {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}. + * @hide PROPOSED_NAN_API + */ +public class SubscribeData implements Parcelable { + /** + * @hide + */ + public final String mServiceName; + + /** + * @hide + */ + public final int mServiceSpecificInfoLength; + + /** + * @hide + */ + public final byte[] mServiceSpecificInfo; + + /** + * @hide + */ + public final int mTxFilterLength; + + /** + * @hide + */ + public final byte[] mTxFilter; + + /** + * @hide + */ + public final int mRxFilterLength; + + /** + * @hide + */ + public final byte[] mRxFilter; + + private SubscribeData(String serviceName, byte[] serviceSpecificInfo, + int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter, + int rxFilterLength) { + mServiceName = serviceName; + mServiceSpecificInfoLength = serviceSpecificInfoLength; + mServiceSpecificInfo = serviceSpecificInfo; + mTxFilterLength = txFilterLength; + mTxFilter = txFilter; + mRxFilterLength = rxFilterLength; + mRxFilter = rxFilter; + } + + @Override + public String toString() { + return "SubscribeData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='" + + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength)) + + "', mTxFilter=" + + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString() + + ", mRxFilter=" + + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString() + + "']"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mServiceName); + dest.writeInt(mServiceSpecificInfoLength); + if (mServiceSpecificInfoLength != 0) { + dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength); + } + dest.writeInt(mTxFilterLength); + if (mTxFilterLength != 0) { + dest.writeByteArray(mTxFilter, 0, mTxFilterLength); + } + dest.writeInt(mRxFilterLength); + if (mRxFilterLength != 0) { + dest.writeByteArray(mRxFilter, 0, mRxFilterLength); + } + } + + public static final Creator<SubscribeData> CREATOR = new Creator<SubscribeData>() { + @Override + public SubscribeData[] newArray(int size) { + return new SubscribeData[size]; + } + + @Override + public SubscribeData createFromParcel(Parcel in) { + String serviceName = in.readString(); + int ssiLength = in.readInt(); + byte[] ssi = new byte[ssiLength]; + if (ssiLength != 0) { + in.readByteArray(ssi); + } + int txFilterLength = in.readInt(); + byte[] txFilter = new byte[txFilterLength]; + if (txFilterLength != 0) { + in.readByteArray(txFilter); + } + int rxFilterLength = in.readInt(); + byte[] rxFilter = new byte[rxFilterLength]; + if (rxFilterLength != 0) { + in.readByteArray(rxFilter); + } + + return new SubscribeData(serviceName, ssi, ssiLength, txFilter, txFilterLength, + rxFilter, rxFilterLength); + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof SubscribeData)) { + return false; + } + + SubscribeData lhs = (SubscribeData) o; + + if (!mServiceName.equals(lhs.mServiceName) + || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength + || mTxFilterLength != lhs.mTxFilterLength + || mRxFilterLength != lhs.mRxFilterLength) { + return false; + } + + if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) { + for (int i = 0; i < mServiceSpecificInfoLength; ++i) { + if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) { + return false; + } + } + } else if (mServiceSpecificInfoLength != 0) { + return false; // invalid != invalid + } + + if (mTxFilter != null && lhs.mTxFilter != null) { + for (int i = 0; i < mTxFilterLength; ++i) { + if (mTxFilter[i] != lhs.mTxFilter[i]) { + return false; + } + } + } else if (mTxFilterLength != 0) { + return false; // invalid != invalid + } + + if (mRxFilter != null && lhs.mRxFilter != null) { + for (int i = 0; i < mRxFilterLength; ++i) { + if (mRxFilter[i] != lhs.mRxFilter[i]) { + return false; + } + } + } else if (mRxFilterLength != 0) { + return false; // invalid != invalid + } + + return true; + } + + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + mServiceName.hashCode(); + result = 31 * result + mServiceSpecificInfoLength; + result = 31 * result + Arrays.hashCode(mServiceSpecificInfo); + result = 31 * result + mTxFilterLength; + result = 31 * result + Arrays.hashCode(mTxFilter); + result = 31 * result + mRxFilterLength; + result = 31 * result + Arrays.hashCode(mRxFilter); + + return result; + } + + /** + * Builder used to build {@link SubscribeData} objects. + */ + public static final class Builder { + private String mServiceName; + private int mServiceSpecificInfoLength; + private byte[] mServiceSpecificInfo = new byte[0]; + private int mTxFilterLength; + private byte[] mTxFilter = new byte[0]; + private int mRxFilterLength; + private byte[] mRxFilter = new byte[0]; + + /** + * Specify the service name of the subscribe session. The actual on-air + * value is a 6 byte hashed representation of this string. + * + * @param serviceName The service name for the subscribe session. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setServiceName(String serviceName) { + mServiceName = serviceName; + return this; + } + + /** + * Specify service specific information for the subscribe session. This + * is a free-form byte array available to the application to send + * additional information as part of the discovery operation - i.e. it + * will not be used to determine whether a publish/subscribe match + * occurs. + * + * @param serviceSpecificInfo A byte-array for the service-specific + * information field. + * @param serviceSpecificInfoLength The length of the byte-array to be + * used. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo, + int serviceSpecificInfoLength) { + mServiceSpecificInfoLength = serviceSpecificInfoLength; + mServiceSpecificInfo = serviceSpecificInfo; + return this; + } + + /** + * Specify service specific information for the subscribe session - same + * as {@link SubscribeData.Builder#setServiceSpecificInfo(byte[], int)} + * but obtaining the data from a String. + * + * @param serviceSpecificInfoStr The service specific information string + * to be included (as a byte array) in the subscribe + * information. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) { + mServiceSpecificInfoLength = serviceSpecificInfoStr.length(); + mServiceSpecificInfo = serviceSpecificInfoStr.getBytes(); + return this; + } + + /** + * The transmit filter for an active subscribe session + * {@link SubscribeSettings.Builder#setSubscribeType(int)} and + * {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}. Included in + * transmitted subscribe packets and used by receivers (passive + * publishers) to determine whether they match - in addition to just + * relying on the service name. + * <p> + * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * is available to form and parse. + * + * @param txFilter The byte-array containing the LV formatted transmit + * filter. + * @param txFilterLength The number of bytes in the transmit filter + * argument. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setTxFilter(byte[] txFilter, int txFilterLength) { + mTxFilter = txFilter; + mTxFilterLength = txFilterLength; + return this; + } + + /** + * The transmit filter for a passive subsribe session + * {@link SubscribeSettings.Builder#setSubscribeType(int)} and + * {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}. Used by the + * subscriber to determine whether they match transmitted publish + * packets - in addition to just relying on the service name. + * <p> + * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * is available to form and parse. + * + * @param rxFilter The byte-array containing the LV formatted receive + * filter. + * @param rxFilterLength The number of bytes in the receive filter + * argument. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) { + mRxFilter = rxFilter; + mRxFilterLength = rxFilterLength; + return this; + } + + /** + * Build {@link SubscribeData} given the current requests made on the + * builder. + */ + public SubscribeData build() { + return new SubscribeData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength, + mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl b/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl new file mode 100644 index 000000000000..44849bc04a57 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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.nan; + +parcelable SubscribeSettings; diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.java b/wifi/java/android/net/wifi/nan/SubscribeSettings.java new file mode 100644 index 000000000000..5c4f8fb385fa --- /dev/null +++ b/wifi/java/android/net/wifi/nan/SubscribeSettings.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Defines the settings (configuration) for a NAN subscribe session. Built using + * {@link SubscribeSettings.Builder}. Subscribe is done using + * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)} + * or {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}. + * + * @hide PROPOSED_NAN_API + */ +public class SubscribeSettings implements Parcelable { + + /** + * Defines a passive subscribe session - i.e. a subscribe session where + * subscribe packets are not transmitted over-the-air and the device listens + * and matches to transmitted publish packets. Configuration is done using + * {@link SubscribeSettings.Builder#setSubscribeType(int)}. + */ + public static final int SUBSCRIBE_TYPE_PASSIVE = 0; + + /** + * Defines an active subscribe session - i.e. a subscribe session where + * subscribe packets are transmitted over-the-air. Configuration is done + * using {@link SubscribeSettings.Builder#setSubscribeType(int)}. + */ + public static final int SUBSCRIBE_TYPE_ACTIVE = 1; + + /** + * @hide + */ + public final int mSubscribeType; + + /** + * @hide + */ + public final int mSubscribeCount; + + /** + * @hide + */ + public final int mTtlSec; + + private SubscribeSettings(int subscribeType, int publichCount, int ttlSec) { + mSubscribeType = subscribeType; + mSubscribeCount = publichCount; + mTtlSec = ttlSec; + } + + @Override + public String toString() { + return "SubscribeSettings [mSubscribeType=" + mSubscribeType + ", mSubscribeCount=" + + mSubscribeCount + ", mTtlSec=" + mTtlSec + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSubscribeType); + dest.writeInt(mSubscribeCount); + dest.writeInt(mTtlSec); + } + + public static final Creator<SubscribeSettings> CREATOR = new Creator<SubscribeSettings>() { + @Override + public SubscribeSettings[] newArray(int size) { + return new SubscribeSettings[size]; + } + + @Override + public SubscribeSettings createFromParcel(Parcel in) { + int subscribeType = in.readInt(); + int subscribeCount = in.readInt(); + int ttlSec = in.readInt(); + return new SubscribeSettings(subscribeType, subscribeCount, ttlSec); + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof SubscribeSettings)) { + return false; + } + + SubscribeSettings lhs = (SubscribeSettings) o; + + return mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount + && mTtlSec == lhs.mTtlSec; + } + + @Override + public int hashCode() { + int result = 17; + + result = 31 * result + mSubscribeType; + result = 31 * result + mSubscribeCount; + result = 31 * result + mTtlSec; + + return result; + } + + /** + * Builder used to build {@link SubscribeSettings} objects. + */ + public static final class Builder { + int mSubscribeType; + int mSubscribeCount; + int mTtlSec; + + /** + * Sets the type of the subscribe session: active (subscribe packets are + * transmitted over-the-air), or passive (no subscribe packets are + * transmitted, a match is made against a solicited/active publish + * session whose packets are transmitted over-the-air). + * + * @param subscribeType Subscribe session type: active ( + * {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}) or + * passive ( {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE} + * ). + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setSubscribeType(int subscribeType) { + if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) { + throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType); + } + mSubscribeType = subscribeType; + return this; + } + + /** + * Sets the number of times an active ( + * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe + * session will transmit a packet. When the count is reached an event + * will be generated for + * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason= + * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}. + * + * @param subscribeCount Number of subscribe packets to transmit. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setSubscribeCount(int subscribeCount) { + if (subscribeCount < 0) { + throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative"); + } + mSubscribeCount = subscribeCount; + return this; + } + + /** + * Sets the time interval (in seconds) an active ( + * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe + * session will be alive - i.e. transmitting a packet. When the TTL is + * reached an event will be generated for + * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason= + * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}. + * + * @param ttlSec Lifetime of a subscribe session in seconds. + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + */ + public Builder setTtlSec(int ttlSec) { + if (ttlSec < 0) { + throw new IllegalArgumentException("Invalid ttlSec - must be non-negative"); + } + mTtlSec = ttlSec; + return this; + } + + /** + * Build {@link SubscribeSettings} given the current requests made on + * the builder. + */ + public SubscribeSettings build() { + return new SubscribeSettings(mSubscribeType, mSubscribeCount, mTtlSec); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java new file mode 100644 index 000000000000..ea8785a858ee --- /dev/null +++ b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2016 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.nan; + +import libcore.io.Memory; + +import java.nio.BufferOverflowException; +import java.nio.ByteOrder; +import java.util.Iterator; + +/** + * Utility class to construct and parse byte arrays using the TLV format - + * Type/Length/Value format. The utilities accept a configuration of the size of + * the Type field and the Length field. A Type field size of 0 is allowed - + * allowing usage for LV (no T) array formats. + * + * @hide PROPOSED_NAN_API + */ +public class TlvBufferUtils { + private TlvBufferUtils() { + // no reason to ever create this class + } + + /** + * Utility class to construct byte arrays using the TLV format - + * Type/Length/Value. + * <p> + * A constructor is created specifying the size of the Type (T) and Length + * (L) fields. A specification of zero size T field is allowed - resulting + * in LV type format. + * <p> + * The byte array is either provided (using + * {@link TlvConstructor#wrap(byte[])}) or allocated (using + * {@link TlvConstructor#allocate(int)}). + * <p> + * Values are added to the structure using the {@code TlvConstructor.put*()} + * methods. + * <p> + * The final byte array is obtained using {@link TlvConstructor#getArray()} + * and {@link TlvConstructor#getActualLength()} methods. + */ + public static class TlvConstructor { + private int mTypeSize; + private int mLengthSize; + + private byte[] mArray; + private int mArrayLength; + private int mPosition; + + /** + * Define a TLV constructor with the specified size of the Type (T) and + * Length (L) fields. + * + * @param typeSize Number of bytes used for the Type (T) field. Values + * of 0, 1, or 2 bytes are allowed. A specification of 0 + * bytes implies that the field being constructed has the LV + * format rather than the TLV format. + * @param lengthSize Number of bytes used for the Length (L) field. + * Values of 1 or 2 bytes are allowed. + */ + public TlvConstructor(int typeSize, int lengthSize) { + if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) { + throw new IllegalArgumentException( + "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize); + } + mTypeSize = typeSize; + mLengthSize = lengthSize; + } + + /** + * Set the byte array to be used to construct the TLV. + * + * @param array Byte array to be formatted. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor wrap(byte[] array) { + mArray = array; + mArrayLength = array.length; + return this; + } + + /** + * Allocates a new byte array to be used ot construct a TLV. + * + * @param capacity The size of the byte array to be allocated. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor allocate(int capacity) { + mArray = new byte[capacity]; + mArrayLength = capacity; + return this; + } + + /** + * Copies a byte into the TLV with the indicated type. For an LV + * formatted structure (i.e. typeLength=0 in {@link TlvConstructor + * TlvConstructor(int, int)} ) the type field is ignored. + * + * @param type The value to be placed into the Type field. + * @param b The byte to be inserted into the structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putByte(int type, byte b) { + checkLength(1); + addHeader(type, 1); + mArray[mPosition++] = b; + return this; + } + + /** + * Copies a byte array into the TLV with the indicated type. For an LV + * formatted structure (i.e. typeLength=0 in {@link TlvConstructor + * TlvConstructor(int, int)} ) the type field is ignored. + * + * @param type The value to be placed into the Type field. + * @param array The array to be copied into the TLV structure. + * @param offset Start copying from the array at the specified offset. + * @param length Copy the specified number (length) of bytes from the + * array. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putByteArray(int type, byte[] array, int offset, int length) { + checkLength(length); + addHeader(type, length); + System.arraycopy(array, offset, mArray, mPosition, length); + mPosition += length; + return this; + } + + /** + * Copies a byte array into the TLV with the indicated type. For an LV + * formatted structure (i.e. typeLength=0 in {@link TlvConstructor + * TlvConstructor(int, int)} ) the type field is ignored. + * + * @param type The value to be placed into the Type field. + * @param array The array to be copied (in full) into the TLV structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putByteArray(int type, byte[] array) { + return putByteArray(type, array, 0, array.length); + } + + /** + * Places a zero length element (i.e. Length field = 0) into the TLV. + * For an LV formatted structure (i.e. typeLength=0 in + * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is + * ignored. + * + * @param type The value to be placed into the Type field. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putZeroLengthElement(int type) { + checkLength(0); + addHeader(type, 0); + return this; + } + + /** + * Copies short into the TLV with the indicated type. For an LV + * formatted structure (i.e. typeLength=0 in {@link TlvConstructor + * TlvConstructor(int, int)} ) the type field is ignored. + * + * @param type The value to be placed into the Type field. + * @param data The short to be inserted into the structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putShort(int type, short data) { + checkLength(2); + addHeader(type, 2); + Memory.pokeShort(mArray, mPosition, data, ByteOrder.BIG_ENDIAN); + mPosition += 2; + return this; + } + + /** + * Copies integer into the TLV with the indicated type. For an LV + * formatted structure (i.e. typeLength=0 in {@link TlvConstructor + * TlvConstructor(int, int)} ) the type field is ignored. + * + * @param type The value to be placed into the Type field. + * @param data The integer to be inserted into the structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putInt(int type, int data) { + checkLength(4); + addHeader(type, 4); + Memory.pokeInt(mArray, mPosition, data, ByteOrder.BIG_ENDIAN); + mPosition += 4; + return this; + } + + /** + * Copies a String's byte representation into the TLV with the indicated + * type. For an LV formatted structure (i.e. typeLength=0 in + * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is + * ignored. + * + * @param type The value to be placed into the Type field. + * @param data The string whose bytes are to be inserted into the + * structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public TlvConstructor putString(int type, String data) { + return putByteArray(type, data.getBytes(), 0, data.length()); + } + + /** + * Returns the constructed TLV formatted byte-array. Note that the + * returned array is the fully wrapped ( + * {@link TlvConstructor#wrap(byte[])}) or allocated ( + * {@link TlvConstructor#allocate(int)}) array - which isn't necessarily + * the actual size of the formatted data. Use + * {@link TlvConstructor#getActualLength()} to obtain the size of the + * formatted data. + * + * @return The byte array containing the TLV formatted structure. + */ + public byte[] getArray() { + return mArray; + } + + /** + * Returns the size of the TLV formatted portion of the wrapped or + * allocated byte array. The array itself is returned with + * {@link TlvConstructor#getArray()}. + * + * @return The size of the TLV formatted portion of the byte array. + */ + public int getActualLength() { + return mPosition; + } + + private void checkLength(int dataLength) { + if (mPosition + mTypeSize + mLengthSize + dataLength > mArrayLength) { + throw new BufferOverflowException(); + } + } + + private void addHeader(int type, int length) { + if (mTypeSize == 1) { + mArray[mPosition] = (byte) type; + } else if (mTypeSize == 2) { + Memory.pokeShort(mArray, mPosition, (short) type, ByteOrder.BIG_ENDIAN); + } + mPosition += mTypeSize; + + if (mLengthSize == 1) { + mArray[mPosition] = (byte) length; + } else if (mLengthSize == 2) { + Memory.pokeShort(mArray, mPosition, (short) length, ByteOrder.BIG_ENDIAN); + } + mPosition += mLengthSize; + } + } + + /** + * Utility class used when iterating over a TLV formatted byte-array. Use + * {@link TlvIterable} to iterate over array. A {@link TlvElement} + * represents each entry in a TLV formatted byte-array. + */ + public static class TlvElement { + /** + * The Type (T) field of the current TLV element. Note that for LV + * formatted byte-arrays (i.e. TLV whose Type/T size is 0) the value of + * this field is undefined. + */ + public int mType; + + /** + * The Length (L) field of the current TLV element. + */ + public int mLength; + + /** + * The Value (V) field - a raw byte array representing the current TLV + * element where the entry starts at {@link TlvElement#mOffset}. + */ + public byte[] mRefArray; + + /** + * The offset to be used into {@link TlvElement#mRefArray} to access the + * raw data representing the current TLV element. + */ + public int mOffset; + + private TlvElement(int type, int length, byte[] refArray, int offset) { + mType = type; + mLength = length; + mRefArray = refArray; + mOffset = offset; + } + + /** + * Utility function to return a byte representation of a TLV element of + * length 1. Note: an attempt to call this function on a TLV item whose + * {@link TlvElement#mLength} is != 1 will result in an exception. + * + * @return byte representation of current TLV element. + */ + public byte getByte() { + if (mLength != 1) { + throw new IllegalArgumentException( + "Accesing a byte from a TLV element of length " + mLength); + } + return mRefArray[mOffset]; + } + + /** + * Utility function to return a short representation of a TLV element of + * length 2. Note: an attempt to call this function on a TLV item whose + * {@link TlvElement#mLength} is != 2 will result in an exception. + * + * @return short representation of current TLV element. + */ + public short getShort() { + if (mLength != 2) { + throw new IllegalArgumentException( + "Accesing a short from a TLV element of length " + mLength); + } + return Memory.peekShort(mRefArray, mOffset, ByteOrder.BIG_ENDIAN); + } + + /** + * Utility function to return an integer representation of a TLV element + * of length 4. Note: an attempt to call this function on a TLV item + * whose {@link TlvElement#mLength} is != 4 will result in an exception. + * + * @return integer representation of current TLV element. + */ + public int getInt() { + if (mLength != 4) { + throw new IllegalArgumentException( + "Accesing an int from a TLV element of length " + mLength); + } + return Memory.peekInt(mRefArray, mOffset, ByteOrder.BIG_ENDIAN); + } + + /** + * Utility function to return a String representation of a TLV element. + * + * @return String repersentation of the current TLV element. + */ + public String getString() { + return new String(mRefArray, mOffset, mLength); + } + } + + /** + * Utility class to iterate over a TLV formatted byte-array. + */ + public static class TlvIterable implements Iterable<TlvElement> { + private int mTypeSize; + private int mLengthSize; + private byte[] mArray; + private int mArrayLength; + + /** + * Constructs a TlvIterable object - specifying the format of the TLV + * (the sizes of the Type and Length fields), and the byte array whose + * data is to be parsed. + * + * @param typeSize Number of bytes used for the Type (T) field. Valid + * values are 0 (i.e. indicating the format is LV rather than + * TLV), 1, and 2 bytes. + * @param lengthSize Number of bytes sued for the Length (L) field. + * Values values are 1 or 2 bytes. + * @param array The TLV formatted byte-array to parse. + * @param length The number of bytes of the array to be used in the + * parsing. + */ + public TlvIterable(int typeSize, int lengthSize, byte[] array, int length) { + if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) { + throw new IllegalArgumentException( + "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize); + } + mTypeSize = typeSize; + mLengthSize = lengthSize; + mArray = array; + mArrayLength = length; + } + + /** + * Prints out a parsed representation of the TLV-formatted byte array. + * Whenever possible bytes, shorts, and integer are printed out (for + * fields whose length is 1, 2, or 4 respectively). + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("["); + boolean first = true; + for (TlvElement tlv : this) { + if (!first) { + builder.append(","); + } + first = false; + builder.append(" ("); + if (mTypeSize != 0) { + builder.append("T=" + tlv.mType + ","); + } + builder.append("L=" + tlv.mLength + ") "); + if (tlv.mLength == 0) { + builder.append("<null>"); + } else if (tlv.mLength == 1) { + builder.append(tlv.getByte()); + } else if (tlv.mLength == 2) { + builder.append(tlv.getShort()); + } else if (tlv.mLength == 4) { + builder.append(tlv.getInt()); + } else { + builder.append("<bytes>"); + } + if (tlv.mLength != 0) { + builder.append(" (S='" + tlv.getString() + "')"); + } + } + builder.append("]"); + + return builder.toString(); + } + + /** + * Returns an iterator to step through a TLV formatted byte-array. The + * individual elements returned by the iterator are {@link TlvElement}. + */ + @Override + public Iterator<TlvElement> iterator() { + return new Iterator<TlvElement>() { + private int mOffset = 0; + + @Override + public boolean hasNext() { + return mOffset < mArrayLength; + } + + @Override + public TlvElement next() { + int type = 0; + if (mTypeSize == 1) { + type = mArray[mOffset]; + } else if (mTypeSize == 2) { + type = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN); + } + mOffset += mTypeSize; + + int length = 0; + if (mLengthSize == 1) { + length = mArray[mOffset]; + } else if (mLengthSize == 2) { + length = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN); + } + mOffset += mLengthSize; + + TlvElement tlv = new TlvElement(type, length, mArray, mOffset); + mOffset += length; + return tlv; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java new file mode 100644 index 000000000000..eae0a55f7af1 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +/** + * Base class for NAN events callbacks. Should be extended by applications + * wanting notifications. These are callbacks applying to the NAN connection as + * a whole - not to specific publish or subscribe sessions - for that see + * {@link WifiNanSessionListener}. + * <p> + * During registration specify which specific events are desired using a set of + * {@code NanEventListener.LISTEN_*} flags OR'd together. Only those events will + * be delivered to the registered listener. Override those callbacks + * {@code NanEventListener.on*} for the registered events. + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanEventListener { + private static final String TAG = "WifiNanEventListener"; + private static final boolean DBG = true; + private static final boolean VDBG = false; // STOPSHIP if true + + /** + * Configuration completion callback event registration flag. Corresponding + * callback is {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)}. + */ + public static final int LISTEN_CONFIG_COMPLETED = 0x1 << 0; + + /** + * Configuration failed callback event registration flag. Corresponding + * callback is {@link WifiNanEventListener#onConfigFailed(int)}. + */ + public static final int LISTEN_CONFIG_FAILED = 0x1 << 1; + + /** + * NAN cluster is down callback event registration flag. Corresponding + * callback is {@link WifiNanEventListener#onNanDown(int)}. + */ + public static final int LISTEN_NAN_DOWN = 0x1 << 2; + + /** + * NAN identity has changed event registration flag. This may be due to + * joining a cluster, starting a cluster, or discovery interface change. The + * implication is that peers you've been communicating with may no longer + * recognize you and you need to re-establish your identity. Corresponding + * callback is {@link WifiNanEventListener#onIdentityChanged()}. + */ + public static final int LISTEN_IDENTITY_CHANGED = 0x1 << 3; + + private final Handler mHandler; + + /** + * Constructs a {@link WifiNanEventListener} using the looper of the current + * thread. I.e. all callbacks will be delivered on the current thread. + */ + public WifiNanEventListener() { + this(Looper.myLooper()); + } + + /** + * Constructs a {@link WifiNanEventListener} using the specified looper. I.e. + * all callbacks will delivered on the thread of the specified looper. + * + * @param looper The looper on which to execute the callbacks. + */ + public WifiNanEventListener(Looper looper) { + if (VDBG) Log.v(TAG, "ctor: looper=" + looper); + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg); + switch (msg.what) { + case LISTEN_CONFIG_COMPLETED: + WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj); + break; + case LISTEN_CONFIG_FAILED: + WifiNanEventListener.this.onConfigFailed(msg.arg1); + break; + case LISTEN_NAN_DOWN: + WifiNanEventListener.this.onNanDown(msg.arg1); + break; + case LISTEN_IDENTITY_CHANGED: + WifiNanEventListener.this.onIdentityChanged(); + break; + } + } + }; + } + + /** + * Called when NAN configuration is completed. Event will only be delivered + * if registered using {@link WifiNanEventListener#LISTEN_CONFIG_COMPLETED}. A + * dummy (empty implementation printing out a warning). Make sure to + * override if registered. + * + * @param completedConfig The actual configuration request which was + * completed. Note that it may be different from that requested + * by the application. The service combines configuration + * requests from all applications. + */ + public void onConfigCompleted(ConfigRequest completedConfig) { + Log.w(TAG, "onConfigCompleted: called in stub - override if interested or disable"); + } + + /** + * Called when NAN configuration failed. Event will only be delivered if + * registered using {@link WifiNanEventListener#LISTEN_CONFIG_FAILED}. A dummy + * (empty implementation printing out a warning). Make sure to override if + * registered. + * + * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}. + */ + public void onConfigFailed(int reason) { + Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable"); + } + + /** + * Called when NAN cluster is down. Event will only be delivered if + * registered using {@link WifiNanEventListener#LISTEN_NAN_DOWN}. A dummy (empty + * implementation printing out a warning). Make sure to override if + * registered. + * + * @param reason Reason code for event, see {@code NanSessionListener.FAIL_*}. + */ + public void onNanDown(int reason) { + Log.w(TAG, "onNanDown: called in stub - override if interested or disable"); + } + + /** + * Called when NAN identity has changed. This may be due to joining a + * cluster, starting a cluster, or discovery interface change. The + * implication is that peers you've been communicating with may no longer + * recognize you and you need to re-establish your identity. Event will only + * be delivered if registered using + * {@link WifiNanEventListener#LISTEN_IDENTITY_CHANGED}. A dummy (empty + * implementation printing out a warning). Make sure to override if + * registered. + */ + public void onIdentityChanged() { + if (VDBG) Log.v(TAG, "onIdentityChanged: called in stub - override if interested"); + } + + /** + * {@hide} + */ + public IWifiNanEventListener callback = new IWifiNanEventListener.Stub() { + @Override + public void onConfigCompleted(ConfigRequest completedConfig) { + if (VDBG) Log.v(TAG, "onConfigCompleted: configRequest=" + completedConfig); + + Message msg = mHandler.obtainMessage(LISTEN_CONFIG_COMPLETED); + msg.obj = completedConfig; + mHandler.sendMessage(msg); + } + + @Override + public void onConfigFailed(int reason) { + if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onNanDown(int reason) { + if (VDBG) Log.v(TAG, "onNanDown: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_NAN_DOWN); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onIdentityChanged() { + if (VDBG) Log.v(TAG, "onIdentityChanged"); + + Message msg = mHandler.obtainMessage(LISTEN_IDENTITY_CHANGED); + mHandler.sendMessage(msg); + } + }; +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java new file mode 100644 index 000000000000..877f9937bbb9 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +/** + * This class provides the primary API for managing Wi-Fi NAN operation: + * including discovery and data-links. Get an instance of this class by calling + * {@link android.content.Context#getSystemService(String) + * Context.getSystemService(Context.WIFI_NAN_SERVICE)}. + * <p> + * The class provides access to: + * <ul> + * <li>Configure a NAN connection and register for events. + * <li>Create publish and subscribe sessions. + * <li>Create NAN network specifier to be used to create a NAN network. + * </ul> + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanManager { + private static final String TAG = "WifiNanManager"; + private static final boolean DBG = true; + private static final boolean VDBG = false; // STOPSHIP if true + + private IBinder mBinder; + + private IWifiNanManager mService; + + /** + * {@hide} + */ + public WifiNanManager(IWifiNanManager service) { + mService = service; + } + + /** + * Re-connect to the Wi-Fi NAN service - enabling the application to execute + * {@link WifiNanManager} APIs. Application don't normally need to call this + * API since it is executed in the constructor. However, applications which + * have explicitly {@link WifiNanManager#disconnect()} need to call this + * function to re-connect. + * + * @param listener A listener extended from {@link WifiNanEventListener}. + * @param events The set of events to be delivered to the {@code listener}. + * OR'd event flags from {@link WifiNanEventListener + * NanEventListener.LISTEN*}. + */ + public void connect(WifiNanEventListener listener, int events) { + try { + if (VDBG) Log.v(TAG, "connect()"); + if (listener == null) { + throw new IllegalArgumentException("Invalid listener - must not be null"); + } + if (mBinder == null) { + mBinder = new Binder(); + } + mService.connect(mBinder, listener.callback, events); + } catch (RemoteException e) { + Log.w(TAG, "connect RemoteException (FYI - ignoring): " + e); + } + } + + /** + * Disconnect from the Wi-Fi NAN service and destroy all outstanding + * operations - i.e. all publish and subscribes are terminated, any + * outstanding data-link is shut-down, and all requested NAN configurations + * are cancelled. + * <p> + * An application may then re-connect using + * {@link WifiNanManager#connect(WifiNanEventListener, int)} . + */ + public void disconnect() { + try { + if (VDBG) Log.v(TAG, "disconnect()"); + mService.disconnect(mBinder); + mBinder = null; + } catch (RemoteException e) { + Log.w(TAG, "disconnect RemoteException (FYI - ignoring): " + e); + } + } + + /** + * Requests a NAN configuration, specified by {@link ConfigRequest}. Note + * that NAN is a shared resource and the device can only be a member of a + * single cluster. Thus the service may merge configuration requests from + * multiple applications and configure NAN differently from individual + * requests. + * <p> + * The {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)} will be + * called when configuration is completed (if a listener is registered for + * this specific event). + * + * @param configRequest The requested NAN configuration. + */ + public void requestConfig(ConfigRequest configRequest) { + if (VDBG) Log.v(TAG, "requestConfig(): configRequest=" + configRequest); + try { + mService.requestConfig(configRequest); + } catch (RemoteException e) { + Log.w(TAG, "requestConfig RemoteException (FYI - ignoring): " + e); + } + } + + /** + * Request a NAN publish session. The results of the publish session + * operation will result in callbacks to the indicated listener: + * {@link WifiNanSessionListener NanSessionListener.on*}. + * + * @param publishData The {@link PublishData} specifying the contents of the + * publish session. + * @param publishSettings The {@link PublishSettings} specifying the + * settings for the publish session. + * @param listener The {@link WifiNanSessionListener} derived objects to be used + * for the event callbacks specified by {@code events}. + * @param events The list of events to be delivered to the {@code listener} + * object. An OR'd value of {@link WifiNanSessionListener + * NanSessionListener.LISTEN_*}. + * @return The {@link WifiNanPublishSession} which can be used to further + * control the publish session. + */ + public WifiNanPublishSession publish(PublishData publishData, PublishSettings publishSettings, + WifiNanSessionListener listener, int events) { + return publishRaw(publishData, publishSettings, listener, + events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS); + } + + /** + * Same as publish(*) but does not modify the event flag + * + * @hide + */ + public WifiNanPublishSession publishRaw(PublishData publishData, + PublishSettings publishSettings, WifiNanSessionListener listener, int events) { + if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings); + + if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED + && publishData.mRxFilterLength != 0) { + throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED " + + "publishes (active) can't have an Rx filter"); + } + if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED + && publishData.mTxFilterLength != 0) { + throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED " + + "publishes (passive) can't have a Tx filter"); + } + if (listener == null) { + throw new IllegalArgumentException("Invalid listener - must not be null"); + } + + int sessionId; + + try { + sessionId = mService.createSession(listener.callback, events); + if (DBG) Log.d(TAG, "publish: session created - sessionId=" + sessionId); + mService.publish(sessionId, publishData, publishSettings); + } catch (RemoteException e) { + Log.w(TAG, "createSession/publish RemoteException: " + e); + return null; + } + + return new WifiNanPublishSession(this, sessionId); + } + + /** + * {@hide} + */ + public void publish(int sessionId, PublishData publishData, PublishSettings publishSettings) { + if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings); + + if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED + && publishData.mRxFilterLength != 0) { + throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED " + + "publishes (active) can't have an Rx filter"); + } + if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED + && publishData.mTxFilterLength != 0) { + throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED " + + "publishes (passive) can't have a Tx filter"); + } + + try { + mService.publish(sessionId, publishData, publishSettings); + } catch (RemoteException e) { + Log.w(TAG, "publish RemoteException: " + e); + } + } + /** + * Request a NAN subscribe session. The results of the subscribe session + * operation will result in callbacks to the indicated listener: + * {@link WifiNanSessionListener NanSessionListener.on*}. + * + * @param subscribeData The {@link SubscribeData} specifying the contents of + * the subscribe session. + * @param subscribeSettings The {@link SubscribeSettings} specifying the + * settings for the subscribe session. + * @param listener The {@link WifiNanSessionListener} derived objects to be used + * for the event callbacks specified by {@code events}. + * @param events The list of events to be delivered to the {@code listener} + * object. An OR'd value of {@link WifiNanSessionListener + * NanSessionListener.LISTEN_*}. + * @return The {@link WifiNanSubscribeSession} which can be used to further + * control the subscribe session. + */ + public WifiNanSubscribeSession subscribe(SubscribeData subscribeData, + SubscribeSettings subscribeSettings, + WifiNanSessionListener listener, int events) { + return subscribeRaw(subscribeData, subscribeSettings, listener, + events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS); + } + + /** + * Same as subscribe(*) but does not modify the event flag + * + * @hide + */ + public WifiNanSubscribeSession subscribeRaw(SubscribeData subscribeData, + SubscribeSettings subscribeSettings, WifiNanSessionListener listener, int events) { + if (VDBG) { + Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings); + } + + if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE + && subscribeData.mRxFilterLength != 0) { + throw new IllegalArgumentException( + "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter"); + } + if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE + && subscribeData.mTxFilterLength != 0) { + throw new IllegalArgumentException( + "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter"); + } + + int sessionId; + + try { + sessionId = mService.createSession(listener.callback, events); + if (DBG) Log.d(TAG, "subscribe: session created - sessionId=" + sessionId); + mService.subscribe(sessionId, subscribeData, subscribeSettings); + } catch (RemoteException e) { + Log.w(TAG, "createSession/subscribe RemoteException: " + e); + return null; + } + + return new WifiNanSubscribeSession(this, sessionId); + } + + /** + * {@hide} + */ + public void subscribe(int sessionId, SubscribeData subscribeData, + SubscribeSettings subscribeSettings) { + if (VDBG) { + Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings); + } + + if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE + && subscribeData.mRxFilterLength != 0) { + throw new IllegalArgumentException( + "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter"); + } + if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE + && subscribeData.mTxFilterLength != 0) { + throw new IllegalArgumentException( + "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter"); + } + + try { + mService.subscribe(sessionId, subscribeData, subscribeSettings); + } catch (RemoteException e) { + Log.w(TAG, "subscribe RemoteException: " + e); + } + } + + /** + * {@hide} + */ + public void stopSession(int sessionId) { + if (DBG) Log.d(TAG, "Stop NAN session #" + sessionId); + + try { + mService.stopSession(sessionId); + } catch (RemoteException e) { + Log.w(TAG, "stopSession RemoteException (FYI - ignoring): " + e); + } + } + + /** + * {@hide} + */ + public void destroySession(int sessionId) { + if (DBG) Log.d(TAG, "Destroy NAN session #" + sessionId); + + try { + mService.destroySession(sessionId); + } catch (RemoteException e) { + Log.w(TAG, "destroySession RemoteException (FYI - ignoring): " + e); + } + } + + /** + * {@hide} + */ + public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength) { + try { + if (VDBG) { + Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId + + ", messageLength=" + messageLength); + } + mService.sendMessage(sessionId, peerId, message, messageLength); + } catch (RemoteException e) { + Log.w(TAG, "subscribe RemoteException (FYI - ignoring): " + e); + } + } +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java new file mode 100644 index 000000000000..81b38f4c12a3 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 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.nan; + +/** + * A representation of a NAN publish session. Created when + * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)} + * is executed. The object can be used to stop and re-start (re-configure) the + * publish session. + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanPublishSession extends WifiNanSession { + /** + * {@hide} + */ + public WifiNanPublishSession(WifiNanManager manager, int sessionId) { + super(manager, sessionId); + } + + /** + * Restart/re-configure the publish session. Note that the + * {@link WifiNanSessionListener} is not replaced - the same listener used at + * creation is still used. + * + * @param publishData The data ({@link PublishData}) to publish. + * @param publishSettings The settings ({@link PublishSettings}) of the + * publish session. + */ + public void publish(PublishData publishData, PublishSettings publishSettings) { + mManager.publish(mSessionId, publishData, publishSettings); + } +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java new file mode 100644 index 000000000000..c6b384e3ae00 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.util.Log; + +/** + * A representation of a single publish or subscribe NAN session. This object + * will not be created directly - only its child classes are available: + * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}. + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanSession { + private static final String TAG = "WifiNanSession"; + private static final boolean DBG = true; + private static final boolean VDBG = false; // STOPSHIP if true + + /** + * {@hide} + */ + protected WifiNanManager mManager; + + /** + * {@hide} + */ + protected int mSessionId; + + /** + * {@hide} + */ + private boolean mDestroyed; + + /** + * {@hide} + */ + public WifiNanSession(WifiNanManager manager, int sessionId) { + if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId); + + mManager = manager; + mSessionId = sessionId; + mDestroyed = false; + } + + /** + * Terminate the current publish or subscribe session - i.e. stop + * transmitting packet on-air (for an active session) or listening for + * matches (for a passive session). Note that the session may still receive + * incoming messages and may be re-configured/re-started at a later time. + */ + public void stop() { + mManager.stopSession(mSessionId); + } + + /** + * Destroy the current publish or subscribe session. Performs a + * {@link WifiNanSession#stop()} function but in addition destroys the session - + * it will not be able to receive any messages or to be restarted at a later + * time. + */ + public void destroy() { + mManager.destroySession(mSessionId); + mDestroyed = true; + } + + /** + * {@hide} + */ + @Override + protected void finalize() throws Throwable { + if (!mDestroyed) { + Log.w(TAG, "WifiNanSession mSessionId=" + mSessionId + + " was not explicitly destroyed. The session may use resources until " + + "destroyed so step should be done explicitly"); + } + destroy(); + } + + /** + * Sends a message to the specified destination. Message transmission is + * part of the current discovery session - i.e. executed subsequent to a + * publish/subscribe + * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)} + * event. + * + * @param peerId The peer's ID for the message. Must be a result of an + * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)} + * event. + * @param message The message to be transmitted. + * @param messageLength The number of bytes from the {@code message} to be + * transmitted. + */ + public void sendMessage(int peerId, byte[] message, int messageLength) { + mManager.sendMessage(mSessionId, peerId, message, messageLength); + } +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java new file mode 100644 index 000000000000..c9d08c7610f3 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2016 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.nan; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +/** + * Base class for NAN session events callbacks. Should be extended by + * applications wanting notifications. The callbacks are registered when a + * publish or subscribe session is created using + * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)} + * or + * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)} + * . These are callbacks applying to a specific NAN session. Events + * corresponding to the NAN link are delivered using {@link WifiNanEventListener}. + * <p> + * A single listener is registered at session creation - it cannot be replaced. + * <p> + * During registration specify which specific events are desired using a set of + * {@code NanSessionListener.LISTEN_*} flags OR'd together. Only those events + * will be delivered to the registered listener. Override those callbacks + * {@code NanSessionListener.on*} for the registered events. + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanSessionListener { + private static final String TAG = "WifiNanSessionListener"; + private static final boolean DBG = true; + private static final boolean VDBG = false; // STOPSHIP if true + + /** + * Publish fail callback event registration flag. Corresponding callback is + * {@link WifiNanSessionListener#onPublishFail(int)}. + * + * @hide + */ + public static final int LISTEN_PUBLISH_FAIL = 0x1 << 0; + + /** + * Publish terminated callback event registration flag. Corresponding + * callback is {@link WifiNanSessionListener#onPublishTerminated(int)}. + */ + public static final int LISTEN_PUBLISH_TERMINATED = 0x1 << 1; + + /** + * Subscribe fail callback event registration flag. Corresponding callback + * is {@link WifiNanSessionListener#onSubscribeFail(int)}. + * + * @hide + */ + public static final int LISTEN_SUBSCRIBE_FAIL = 0x1 << 2; + + /** + * Subscribe terminated callback event registration flag. Corresponding + * callback is {@link WifiNanSessionListener#onSubscribeTerminated(int)}. + */ + public static final int LISTEN_SUBSCRIBE_TERMINATED = 0x1 << 3; + + /** + * Match (discovery: publish or subscribe) callback event registration flag. + * Corresponding callback is + * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}. + * + * @hide + */ + public static final int LISTEN_MATCH = 0x1 << 4; + + /** + * Message sent successfully callback event registration flag. Corresponding + * callback is {@link WifiNanSessionListener#onMessageSendSuccess()}. + * + * @hide + */ + public static final int LISTEN_MESSAGE_SEND_SUCCESS = 0x1 << 5; + + /** + * Message sending failure callback event registration flag. Corresponding + * callback is {@link WifiNanSessionListener#onMessageSendFail(int)}. + * + * @hide + */ + public static final int LISTEN_MESSAGE_SEND_FAIL = 0x1 << 6; + + /** + * Message received callback event registration flag. Corresponding callback + * is {@link WifiNanSessionListener#onMessageReceived(int, byte[], int)}. + * + * @hide + */ + public static final int LISTEN_MESSAGE_RECEIVED = 0x1 << 7; + + /** + * List of hidden events: which are mandatory - i.e. they will be added to + * every request. + * + * @hide + */ + public static final int LISTEN_HIDDEN_FLAGS = LISTEN_PUBLISH_FAIL | LISTEN_SUBSCRIBE_FAIL + | LISTEN_MATCH | LISTEN_MESSAGE_SEND_SUCCESS | LISTEN_MESSAGE_SEND_FAIL + | LISTEN_MESSAGE_RECEIVED; + + /** + * Failure reason flag for {@link WifiNanEventListener} and + * {@link WifiNanSessionListener} callbacks. Indicates no resources to execute + * the requested operation. + */ + public static final int FAIL_REASON_NO_RESOURCES = 0; + + /** + * Failure reason flag for {@link WifiNanEventListener} and + * {@link WifiNanSessionListener} callbacks. Indicates invalid argument in the + * requested operation. + */ + public static final int FAIL_REASON_INVALID_ARGS = 1; + + /** + * Failure reason flag for {@link WifiNanEventListener} and + * {@link WifiNanSessionListener} callbacks. Indicates a message is transmitted + * without a match (i.e. a discovery) occurring first. + */ + public static final int FAIL_REASON_NO_MATCH_SESSION = 2; + + /** + * Failure reason flag for {@link WifiNanEventListener} and + * {@link WifiNanSessionListener} callbacks. Indicates an unspecified error + * occurred during the operation. + */ + public static final int FAIL_REASON_OTHER = 3; + + /** + * Failure reason flag for + * {@link WifiNanSessionListener#onPublishTerminated(int)} and + * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks. + * Indicates that publish or subscribe session is done - i.e. all the + * requested operations (per {@link PublishSettings} or + * {@link SubscribeSettings}) have been executed. + */ + public static final int TERMINATE_REASON_DONE = 0; + + /** + * Failure reason flag for + * {@link WifiNanSessionListener#onPublishTerminated(int)} and + * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks. + * Indicates that publish or subscribe session is terminated due to a + * failure. + */ + public static final int TERMINATE_REASON_FAIL = 1; + + private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id"; + private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message"; + private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2"; + + private final Handler mHandler; + + /** + * Constructs a {@link WifiNanSessionListener} using the looper of the current + * thread. I.e. all callbacks will be delivered on the current thread. + */ + public WifiNanSessionListener() { + this(Looper.myLooper()); + } + + /** + * Constructs a {@link WifiNanSessionListener} using the specified looper. I.e. + * all callbacks will delivered on the thread of the specified looper. + * + * @param looper The looper on which to execute the callbacks. + */ + public WifiNanSessionListener(Looper looper) { + if (VDBG) Log.v(TAG, "ctor: looper=" + looper); + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg); + switch (msg.what) { + case LISTEN_PUBLISH_FAIL: + WifiNanSessionListener.this.onPublishFail(msg.arg1); + break; + case LISTEN_PUBLISH_TERMINATED: + WifiNanSessionListener.this.onPublishTerminated(msg.arg1); + break; + case LISTEN_SUBSCRIBE_FAIL: + WifiNanSessionListener.this.onSubscribeFail(msg.arg1); + break; + case LISTEN_SUBSCRIBE_TERMINATED: + WifiNanSessionListener.this.onSubscribeTerminated(msg.arg1); + break; + case LISTEN_MATCH: + WifiNanSessionListener.this.onMatch( + msg.getData().getInt(MESSAGE_BUNDLE_KEY_PEER_ID), + msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1, + msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2), msg.arg2); + break; + case LISTEN_MESSAGE_SEND_SUCCESS: + WifiNanSessionListener.this.onMessageSendSuccess(); + break; + case LISTEN_MESSAGE_SEND_FAIL: + WifiNanSessionListener.this.onMessageSendFail(msg.arg1); + break; + case LISTEN_MESSAGE_RECEIVED: + WifiNanSessionListener.this.onMessageReceived(msg.arg2, + msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1); + break; + } + } + }; + } + + /** + * Called when a publish operation fails. It is dummy method (empty + * implementation printing out a log message). Override to implement your + * custom response. + * + * @param reason The failure reason using {@code NanSessionListener.FAIL_*} + * codes. + */ + public void onPublishFail(int reason) { + if (VDBG) Log.v(TAG, "onPublishFail: called in stub - override if interested"); + } + + /** + * Called when a publish operation terminates. Event will only be delivered + * if registered using {@link WifiNanSessionListener#LISTEN_PUBLISH_TERMINATED}. + * A dummy (empty implementation printing out a warning). Make sure to + * override if registered. + * + * @param reason The termination reason using + * {@code NanSessionListener.TERMINATE_*} codes. + */ + public void onPublishTerminated(int reason) { + Log.w(TAG, "onPublishTerminated: called in stub - override if interested or disable"); + } + + /** + * Called when a subscribe operation fails. It is dummy method (empty + * implementation printing out a log message). Override to implement your + * custom response. + * + * @param reason The failure reason using {@code NanSessionListener.FAIL_*} + * codes. + */ + public void onSubscribeFail(int reason) { + if (VDBG) Log.v(TAG, "onSubscribeFail: called in stub - override if interested"); + } + + /** + * Called when a subscribe operation terminates. Event will only be + * delivered if registered using + * {@link WifiNanSessionListener#LISTEN_SUBSCRIBE_TERMINATED}. A dummy (empty + * implementation printing out a warning). Make sure to override if + * registered. + * + * @param reason The termination reason using + * {@code NanSessionListener.TERMINATE_*} codes. + */ + public void onSubscribeTerminated(int reason) { + Log.w(TAG, "onSubscribeTerminated: called in stub - override if interested or disable"); + } + + /** + * Called when a discovery (publish or subscribe) operation results in a + * match - i.e. when a peer is discovered. It is dummy method (empty + * implementation printing out a log message). Override to implement your + * custom response. + * + * @param peerId The ID of the peer matching our discovery operation. + * @param serviceSpecificInfo The service specific information (arbitrary + * byte array) provided by the peer as part of its discovery + * packet. + * @param serviceSpecificInfoLength The length of the service specific + * information array. + * @param matchFilter The filter (Tx on advertiser and Rx on listener) which + * resulted in this match. + * @param matchFilterLength The length of the match filter array. + */ + public void onMatch(int peerId, byte[] serviceSpecificInfo, + int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) { + if (VDBG) Log.v(TAG, "onMatch: called in stub - override if interested"); + } + + /** + * Called when a message is transmitted successfully - i.e. when we know + * that it was received successfully (corresponding to an ACK being + * received). It is dummy method (empty implementation printing out a log + * message). Override to implement your custom response. + * <p> + * Note that either this callback or + * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received - + * never both. + */ + public void onMessageSendSuccess() { + if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested"); + } + + /** + * Called when a message transmission fails - i.e. when no ACK is received. + * The hardware will usually attempt to re-transmit several times - this + * event is received after all retries are exhausted. There is a possibility + * that message was received by the destination successfully but the ACK was + * lost. It is dummy method (empty implementation printing out a log + * message). Override to implement your custom response. + * <p> + * Note that either this callback or + * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received - + * never both + * + * @param reason The failure reason using {@code NanSessionListener.FAIL_*} + * codes. + */ + public void onMessageSendFail(int reason) { + if (VDBG) Log.v(TAG, "onMessageSendFail: called in stub - override if interested"); + } + + /** + * Called when a message is received from a discovery session peer. It is + * dummy method (empty implementation printing out a log message). Override + * to implement your custom response. + * + * @param peerId The ID of the peer sending the message. + * @param message A byte array containing the message. + * @param messageLength The length of the byte array containing the relevant + * message bytes. + */ + public void onMessageReceived(int peerId, byte[] message, int messageLength) { + if (VDBG) Log.v(TAG, "onMessageReceived: called in stub - override if interested"); + } + + /** + * {@hide} + */ + public IWifiNanSessionListener callback = new IWifiNanSessionListener.Stub() { + @Override + public void onPublishFail(int reason) { + if (VDBG) Log.v(TAG, "onPublishFail: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_FAIL); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onPublishTerminated(int reason) { + if (VDBG) Log.v(TAG, "onPublishResponse: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_TERMINATED); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onSubscribeFail(int reason) { + if (VDBG) Log.v(TAG, "onSubscribeFail: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_FAIL); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onSubscribeTerminated(int reason) { + if (VDBG) Log.v(TAG, "onSubscribeTerminated: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_TERMINATED); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onMatch(int peerId, byte[] serviceSpecificInfo, + int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) { + if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId); + + Bundle data = new Bundle(); + data.putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId); + data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo); + data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter); + + Message msg = mHandler.obtainMessage(LISTEN_MATCH); + msg.arg1 = serviceSpecificInfoLength; + msg.arg2 = matchFilterLength; + msg.setData(data); + mHandler.sendMessage(msg); + } + + @Override + public void onMessageSendSuccess() { + if (VDBG) Log.v(TAG, "onMessageSendSuccess"); + + Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_SUCCESS); + mHandler.sendMessage(msg); + } + + @Override + public void onMessageSendFail(int reason) { + if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason); + + Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_FAIL); + msg.arg1 = reason; + mHandler.sendMessage(msg); + } + + @Override + public void onMessageReceived(int peerId, byte[] message, int messageLength) { + if (VDBG) { + Log.v(TAG, "onMessageReceived: peerId='" + peerId + "', messageLength=" + + messageLength); + } + + Bundle data = new Bundle(); + data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message); + + Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_RECEIVED); + msg.arg1 = messageLength; + msg.arg2 = peerId; + msg.setData(data); + mHandler.sendMessage(msg); + } + }; +} diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java new file mode 100644 index 000000000000..7dfdd32a2f74 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 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.nan; + +/** + * A representation of a NAN subscribe session. Created when + * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)} + * is executed. The object can be used to stop and re-start (re-configure) the + * subscribe session. + * + * @hide PROPOSED_NAN_API + */ +public class WifiNanSubscribeSession extends WifiNanSession { + /** + * {@hide} + */ + public WifiNanSubscribeSession(WifiNanManager manager, int sessionId) { + super(manager, sessionId); + } + + /** + * Restart/re-configure the subscribe session. Note that the + * {@link WifiNanSessionListener} is not replaced - the same listener used at + * creation is still used. + * + * @param subscribeData The data ({@link SubscribeData}) to subscribe. + * @param subscribeSettings The settings ({@link SubscribeSettings}) of the + * subscribe session. + */ + public void subscribe(SubscribeData subscribeData, SubscribeSettings subscribeSettings) { + mManager.subscribe(mSessionId, subscribeData, subscribeSettings); + } +} |