OSDN Git Service

[NAN] Re-factor connect/config flow
authorEtan Cohen <etancohen@google.com>
Fri, 18 Mar 2016 15:43:38 +0000 (08:43 -0700)
committerEtan Cohen <etancohen@google.com>
Fri, 8 Apr 2016 14:55:59 +0000 (07:55 -0700)
Simplify flow: configure and connect in a single call.
Prevent info leak: no longer provide configuration back to caller.
Update error status codes

Bug: 27617910
Bug: 27553226
Bug: 27579450
Bug: 27674927
Change-Id: Id7aba816a074dbbe0cb188cfe498c97dccbdcb27

12 files changed:
wifi/java/android/net/wifi/nan/ConfigRequest.java
wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
wifi/java/android/net/wifi/nan/PublishConfig.java
wifi/java/android/net/wifi/nan/SubscribeConfig.java
wifi/java/android/net/wifi/nan/WifiNanEventCallback.java
wifi/java/android/net/wifi/nan/WifiNanManager.java
wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
wifi/java/android/net/wifi/nan/WifiNanSession.java
wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java
wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java

index de6b8a9..759098e 100644 (file)
@@ -22,9 +22,10 @@ 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.
+ * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback, 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
  */
@@ -146,6 +147,30 @@ public class ConfigRequest implements Parcelable {
                 && mEnableIdentityChangeCallback == lhs.mEnableIdentityChangeCallback;
     }
 
+    /**
+     * Checks for equality of two configuration - but only considering their
+     * on-the-air NAN configuration impact.
+     *
+     * @param o Object to compare to.
+     * @return true if configuration objects have the same on-the-air
+     *         configuration, false otherwise.
+     * @hide
+     */
+    public boolean equalsOnTheAir(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;
@@ -160,6 +185,39 @@ public class ConfigRequest implements Parcelable {
     }
 
     /**
+     * Validates that the contents of the ConfigRequest are valid. Otherwise
+     * throws an IllegalArgumentException.
+     *
+     * @hide
+     */
+    public void validate() throws IllegalArgumentException {
+        if (mMasterPreference < 0) {
+            throw new IllegalArgumentException(
+                    "Master Preference specification must be non-negative");
+        }
+        if (mMasterPreference == 1 || mMasterPreference == 255 || mMasterPreference > 255) {
+            throw new IllegalArgumentException("Master Preference specification must not "
+                    + "exceed 255 or use 1 or 255 (reserved values)");
+        }
+        if (mClusterLow < CLUSTER_ID_MIN) {
+            throw new IllegalArgumentException("Cluster specification must be non-negative");
+        }
+        if (mClusterLow > CLUSTER_ID_MAX) {
+            throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+        }
+        if (mClusterHigh < CLUSTER_ID_MIN) {
+            throw new IllegalArgumentException("Cluster specification must be non-negative");
+        }
+        if (mClusterHigh > CLUSTER_ID_MAX) {
+            throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+        }
+        if (mClusterLow > mClusterHigh) {
+            throw new IllegalArgumentException(
+                    "Invalid argument combination - must have Cluster Low <= Cluster High");
+        }
+    }
+
+    /**
      * Builder used to build {@link ConfigRequest} objects.
      */
     public static final class Builder {
@@ -175,6 +233,7 @@ public class ConfigRequest implements Parcelable {
          * @param support5gBand Support for 5G band is required.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setSupport5gBand(boolean support5gBand) {
             mSupport5gBand = support5gBand;
@@ -188,6 +247,7 @@ public class ConfigRequest implements Parcelable {
          * @param masterPreference The requested master preference
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setMasterPreference(int masterPreference) {
             if (masterPreference < 0) {
@@ -214,6 +274,7 @@ public class ConfigRequest implements Parcelable {
          * @param clusterLow The lower range of the generated cluster ID.
          * @return The builder to facilitate chaining
          *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setClusterLow(int clusterLow) {
             if (clusterLow < CLUSTER_ID_MIN) {
@@ -238,6 +299,7 @@ public class ConfigRequest implements Parcelable {
          * @param clusterHigh The upper range of the generated cluster ID.
          * @return The builder to facilitate chaining
          *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
+         * @hide PROPOSED_NAN_SYSTEM_API
          */
         public Builder setClusterHigh(int clusterHigh) {
             if (clusterHigh < CLUSTER_ID_MIN) {
index c4ba928..1e7f26b 100644 (file)
@@ -25,8 +25,8 @@ import android.net.wifi.nan.ConfigRequest;
  */
 oneway interface IWifiNanEventCallback
 {
-    void onConfigCompleted(in ConfigRequest completedConfig);
-    void onConfigFailed(in ConfigRequest failedConfig, int reason);
+    void onConnectSuccess();
+    void onConnectFail(int reason);
     void onNanDown(int reason);
     void onIdentityChanged();
 }
index 5c64480..29f14da 100644 (file)
@@ -32,9 +32,9 @@ import android.net.wifi.nan.SubscribeConfig;
 interface IWifiNanManager
 {
     // client API
-    int connect(in IBinder binder, in IWifiNanEventCallback callback);
+    int connect(in IBinder binder, in IWifiNanEventCallback callback,
+            in ConfigRequest configRequest);
     void disconnect(int clientId, in IBinder binder);
-    void requestConfig(int clientId, in ConfigRequest configRequest);
 
     void publish(int clientId, in PublishConfig publishConfig, in IWifiNanSessionCallback callback);
     void subscribe(int clientId, in SubscribeConfig subscribeConfig,
index cff3c7e..7162be7 100644 (file)
@@ -24,6 +24,7 @@ package android.net.wifi.nan;
 oneway interface IWifiNanSessionCallback
 {
     void onSessionStarted(int sessionId);
+    void onSessionConfigSuccess();
     void onSessionConfigFail(int reason);
     void onSessionTerminated(int reason);
 
index 01a9ae1..19cd4cf 100644 (file)
@@ -24,8 +24,8 @@ import java.util.Arrays;
 /**
  * Defines the configuration of a NAN publish session. Built using
  * {@link PublishConfig.Builder}. Publish is done using
- * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback, int)} or
- * {@link WifiNanPublishSession#publish(PublishConfig)}.
+ * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or
+ * {@link WifiNanPublishSession#updatePublish(PublishConfig)}.
  *
  * @hide PROPOSED_NAN_API
  */
@@ -265,6 +265,45 @@ public class PublishConfig implements Parcelable {
     }
 
     /**
+     * Validates that the contents of the PublishConfig are valid. Otherwise
+     * throws an IllegalArgumentException.
+     *
+     * @hide
+     */
+    public void validate() throws IllegalArgumentException {
+        if (mServiceSpecificInfoLength != 0 && (mServiceSpecificInfo == null
+                || mServiceSpecificInfo.length < mServiceSpecificInfoLength)) {
+            throw new IllegalArgumentException("Non-matching combination of "
+                    + "serviceSpecificInfo and serviceSpecificInfoLength");
+        }
+        if (mTxFilterLength != 0 && (mTxFilter == null || mTxFilter.length < mTxFilterLength)) {
+            throw new IllegalArgumentException(
+                    "Non-matching combination of txFilter and txFilterLength");
+        }
+        if (mRxFilterLength != 0 && (mRxFilter == null || mRxFilter.length < mRxFilterLength)) {
+            throw new IllegalArgumentException(
+                    "Non-matching combination of rxFilter and rxFilterLength");
+        }
+        if (mPublishType < PUBLISH_TYPE_UNSOLICITED || mPublishType > PUBLISH_TYPE_SOLICITED) {
+            throw new IllegalArgumentException("Invalid publishType - " + mPublishType);
+        }
+        if (mPublishCount < 0) {
+            throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
+        }
+        if (mTtlSec < 0) {
+            throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+        }
+        if (mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED && mRxFilterLength != 0) {
+            throw new IllegalArgumentException("Invalid publish config: UNSOLICITED "
+                    + "publishes (active) can't have an Rx filter");
+        }
+        if (mPublishType == PublishConfig.PUBLISH_TYPE_SOLICITED && mTxFilterLength != 0) {
+            throw new IllegalArgumentException("Invalid publish config: SOLICITED "
+                    + "publishes (passive) can't have a Tx filter");
+        }
+    }
+
+    /**
      * Builder used to build {@link PublishConfig} objects.
      */
     public static final class Builder {
@@ -417,7 +456,7 @@ public class PublishConfig implements Parcelable {
          * Sets the number of times a solicited (
          * {@link PublishConfig.Builder#setPublishType(int)}) publish session
          * will transmit a packet. When the count is reached an event will be
-         * generated for {@link WifiNanSessionCallback#onPublishTerminated(int)}
+         * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)}
          * with reason={@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
          *
          * @param publishCount Number of publish packets to transmit.
@@ -437,7 +476,7 @@ public class PublishConfig implements Parcelable {
          * {@link PublishConfig.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 WifiNanSessionCallback#onPublishTerminated(int)} with reason=
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} with reason=
          * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
          *
          * @param ttlSec Lifetime of a publish session in seconds.
@@ -454,7 +493,7 @@ public class PublishConfig implements Parcelable {
 
         /**
          * Configure whether a publish terminate notification
-         * {@link WifiNanSessionCallback#onPublishTerminated(int)} is reported
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported
          * back to the callback.
          *
          * @param enable If true the terminate callback will be called when the
index fcf509d..50cbc48 100644 (file)
@@ -25,7 +25,7 @@ import java.util.Arrays;
  * Defines the configuration of a NAN subscribe session. Built using
  * {@link SubscribeConfig.Builder}. Subscribe is done using
  * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} or
- * {@link WifiNanSubscribeSession#subscribe(SubscribeConfig)}.
+ * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}.
  *
  * @hide PROPOSED_NAN_API
  */
@@ -287,6 +287,49 @@ public class SubscribeConfig implements Parcelable {
     }
 
     /**
+     * Validates that the contents of the SubscribeConfig are valid. Otherwise
+     * throws an IllegalArgumentException.
+     *
+     * @hide
+     */
+    public void validate() throws IllegalArgumentException {
+        if (mServiceSpecificInfoLength != 0 && (mServiceSpecificInfo == null
+                || mServiceSpecificInfo.length < mServiceSpecificInfoLength)) {
+            throw new IllegalArgumentException("Non-matching combination of "
+                    + "serviceSpecificInfo and serviceSpecificInfoLength");
+        }
+        if (mTxFilterLength != 0 && (mTxFilter == null || mTxFilter.length < mTxFilterLength)) {
+            throw new IllegalArgumentException(
+                    "Non-matching combination of txFilter and txFilterLength");
+        }
+        if (mRxFilterLength != 0 && (mRxFilter == null || mRxFilter.length < mRxFilterLength)) {
+            throw new IllegalArgumentException(
+                    "Non-matching combination of rxFilter and rxFilterLength");
+        }
+        if (mSubscribeType < SUBSCRIBE_TYPE_PASSIVE || mSubscribeType > SUBSCRIBE_TYPE_ACTIVE) {
+            throw new IllegalArgumentException("Invalid subscribeType - " + mSubscribeType);
+        }
+        if (mSubscribeCount < 0) {
+            throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
+        }
+        if (mTtlSec < 0) {
+            throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+        }
+        if (mMatchStyle != MATCH_STYLE_FIRST_ONLY && mMatchStyle != MATCH_STYLE_ALL) {
+            throw new IllegalArgumentException(
+                    "Invalid matchType - must be MATCH_FIRST_ONLY or MATCH_ALL");
+        }
+        if (mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE && mRxFilterLength != 0) {
+            throw new IllegalArgumentException(
+                    "Invalid subscribe config: ACTIVE subscribes can't have an Rx filter");
+        }
+        if (mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE && mTxFilterLength != 0) {
+            throw new IllegalArgumentException(
+                    "Invalid subscribe config: PASSIVE subscribes can't have a Tx filter");
+        }
+    }
+
+    /**
      * Builder used to build {@link SubscribeConfig} objects.
      */
     public static final class Builder {
@@ -332,6 +375,11 @@ public class SubscribeConfig implements Parcelable {
          */
         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;
@@ -374,6 +422,10 @@ public class SubscribeConfig implements Parcelable {
          *         {@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;
@@ -397,6 +449,10 @@ public class SubscribeConfig implements Parcelable {
          *         {@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;
@@ -427,8 +483,8 @@ public class SubscribeConfig implements Parcelable {
          * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe
          * session will transmit a packet. When the count is reached an event
          * will be generated for
-         * {@link WifiNanSessionCallback#onSubscribeTerminated(int)} with
-         * reason= {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} with reason=
+         * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
          *
          * @param subscribeCount Number of subscribe packets to transmit.
          * @return The builder to facilitate chaining
@@ -447,8 +503,8 @@ public class SubscribeConfig implements Parcelable {
          * {@link SubscribeConfig.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 WifiNanSessionCallback#onSubscribeTerminated(int)} with
-         * reason= {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} with reason=
+         * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}.
          *
          * @param ttlSec Lifetime of a subscribe session in seconds.
          * @return The builder to facilitate chaining
@@ -486,7 +542,7 @@ public class SubscribeConfig implements Parcelable {
 
         /**
          * Configure whether a subscribe terminate notification
-         * {@link WifiNanSessionCallback#onSubscribeTerminated(int)} is reported
+         * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported
          * back to the callback.
          *
          * @param enable If true the terminate callback will be called when the
index c243b5b..4129011 100644 (file)
 
 package android.net.wifi.nan;
 
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Base class for NAN events callbacks. Should be extended by applications
  * wanting notifications. These are callbacks applying to the NAN connection as
@@ -25,25 +30,59 @@ package android.net.wifi.nan;
  * @hide PROPOSED_NAN_API
  */
 public class WifiNanEventCallback {
+    @IntDef({
+            REASON_INVALID_ARGS, REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG, REASON_REQUESTED,
+            REASON_OTHER })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventReasonCodes {
+    }
+
     /**
-     * Called when NAN configuration is completed.
-     *
-     * @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.
+     * Failure reason flag for {@link WifiNanEventCallback} callbacks. Indicates
+     * invalid argument in the requested operation.
+     */
+    public static final int REASON_INVALID_ARGS = 1000;
+
+    /**
+     * Failure reason flag for {@link WifiNanEventCallback} callbacks. Indicates
+     * that a {@link ConfigRequest} passed in
+     * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback, ConfigRequest)}
+     * couldn't be applied since other connections already exist with an
+     * incompatible configurations.
+     */
+    public static final int REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG = 1001;
+
+    /**
+     * Reason flag for {@link WifiNanEventCallback#onNanDown(int)} callback.
+     * Indicates NAN is shut-down per user request.
+     */
+    public static final int REASON_REQUESTED = 1002;
+
+    /**
+     * Failure reason flag for {@link WifiNanEventCallback} callbacks. Indicates
+     * an unspecified error occurred during the operation.
+     */
+    public static final int REASON_OTHER = 1003;
+
+    /**
+     * Called when NAN connect operation
+     * {@link WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}
+     * is completed. Doesn't necessarily mean that have joined or started a NAN
+     * cluster. An indication is provided by {@link #onIdentityChanged()}.
      */
-    public void onConfigCompleted(ConfigRequest completedConfig) {
+    public void onConnectSuccess() {
         /* empty */
     }
 
     /**
-     * Called when NAN configuration failed.
+     * Called when NAN connect operation
+     * {@code WifiNanManager#connect(android.os.Looper, WifiNanEventCallback)}
+     * failed.
      *
      * @param reason Failure reason code, see
-     *            {@code WifiNanSessionCallback.FAIL_*}.
+     *            {@code WifiNanEventCallback.REASON_*}.
      */
-    public void onConfigFailed(@SuppressWarnings("unused") ConfigRequest failedConfig, int reason) {
+    public void onConnectFail(@EventReasonCodes int reason) {
         /* empty */
     }
 
@@ -51,9 +90,9 @@ public class WifiNanEventCallback {
      * Called when NAN cluster is down
      *
      * @param reason Reason code for event, see
-     *            {@code WifiNanSessionCallback.FAIL_*}.
+     *            {@code WifiNanEventCallback.REASON_*}.
      */
-    public void onNanDown(int reason) {
+    public void onNanDown(@EventReasonCodes int reason) {
         /* empty */
     }
 
index bb19ee7..1c333f3 100644 (file)
@@ -25,6 +25,10 @@ import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+
 /**
  * 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
@@ -45,9 +49,31 @@ public class WifiNanManager {
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
+    private final IWifiNanManager mService;
+
+    /*
+     * State transitions:
+     * UNCONNECTED -- (connect()) --> CONNECTING -- (onConnectSuccess()) --> CONNECTED
+     * UNCONNECTED -- (connect()) --> CONNECTING -- (onConnectFail()) --> UNCONNECTED
+     * CONNECTED||CONNECTING -- (disconnect()) --> UNCONNECTED
+     * CONNECTED||CONNECTING -- onNanDown() --> UNCONNECTED
+     */
+    private static final int STATE_UNCONNECTED = 0;
+    private static final int STATE_CONNECTING = 1;
+    private static final int STATE_CONNECTED = 2;
+
+    private Object mLock = new Object(); // lock access to the following vars
+
+    @GuardedBy("mLock")
+    private int mState = STATE_UNCONNECTED;
+
+    @GuardedBy("mLock")
     private IBinder mBinder;
-    private int mClientId = -1;
-    private IWifiNanManager mService;
+
+    @GuardedBy("mLock")
+    private int mClientId;
+
+    @GuardedBy("mLock")
     private Looper mLooper;
 
     /**
@@ -58,7 +84,7 @@ public class WifiNanManager {
     }
 
     /**
-     * Re-connect to the Wi-Fi NAN service - enabling the application to execute
+     * Connect to the Wi-Fi NAN service - enabling the application to execute
      * {@link WifiNanManager} APIs.
      *
      * @param looper The Looper on which to execute all callbacks related to the
@@ -67,25 +93,45 @@ public class WifiNanManager {
      * @param callback A callback extended from {@link WifiNanEventCallback}.
      */
     public void connect(Looper looper, WifiNanEventCallback callback) {
-        if (VDBG) Log.v(TAG, "connect()");
-
-        if (callback == null) {
-            throw new IllegalArgumentException("Invalid callback - must not be null");
-        }
+        connect(looper, callback, null);
+    }
 
-        if (mClientId != -1) {
-            Log.e(TAG, "connect(): mClientId=" + mClientId
-                    + ": seems to calling connect() without disconnecting() first!");
-            throw new IllegalStateException("Calling connect() without disconnecting() first!");
+    /**
+     * Connect to the Wi-Fi NAN service - enabling the application to execute
+     * {@link WifiNanManager} APIs. Allows requesting a specific configuration
+     * using {@link ConfigRequest} structure. Limited to privileged access.
+     *
+     * @param looper The Looper on which to execute all callbacks related to the
+     *            connection - including all sessions opened as part of this
+     *            connection.
+     * @param callback A callback extended from {@link WifiNanEventCallback}.
+     * @param configRequest The requested NAN configuration.
+     */
+    public void connect(Looper looper, WifiNanEventCallback callback, ConfigRequest configRequest) {
+        if (VDBG) {
+            Log.v(TAG, "connect(): looper=" + looper + ", callback=" + callback + ", configRequest="
+                    + configRequest);
         }
 
-        mLooper = looper;
-        mBinder = new Binder();
+        synchronized (mLock) {
+            if (mState != STATE_UNCONNECTED) {
+                Log.e(TAG, "connect(): Calling connect() when state != UNCONNECTED!");
+                return;
+            }
 
-        try {
-            mClientId = mService.connect(mBinder, new WifiNanEventCallbackProxy(mLooper, callback));
-        } catch (RemoteException e) {
-            Log.w(TAG, "connect RemoteException (FYI - ignoring): " + e);
+            mLooper = looper;
+            mBinder = new Binder();
+            mState = STATE_CONNECTING;
+
+            try {
+                mClientId = mService.connect(mBinder,
+                        new WifiNanEventCallbackProxy(this, looper, callback), configRequest);
+            } catch (RemoteException e) {
+                mLooper = null;
+                mBinder = null;
+                mState = STATE_UNCONNECTED;
+                e.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -99,62 +145,40 @@ public class WifiNanManager {
      * {@link WifiNanManager#connect(Looper, WifiNanEventCallback)} .
      */
     public void disconnect() {
-        if (mClientId == -1) {
-            /*
-             * Warning only since could be called multiple times as cleaning-up
-             * (and no damage done).
-             */
-            Log.w(TAG, "disconnect(): called without calling connect() first - or called "
-                    + "multiple times.");
-            return;
+        if (VDBG) Log.v(TAG, "disconnect()");
+
+        IBinder binder;
+        int clientId;
+        synchronized (mLock) {
+            if (mState == STATE_UNCONNECTED) {
+                Log.e(TAG, "disconnect(): called while UNCONNECTED - ignored");
+                return;
+            }
+
+            binder = mBinder;
+            clientId = mClientId;
+
+            mState = STATE_UNCONNECTED;
+            mBinder = null;
+            mLooper = null;
+            mClientId = 0;
         }
+
         try {
-            if (VDBG) Log.v(TAG, "disconnect()");
-            mService.disconnect(mClientId, mBinder);
-            mBinder = null;
-            mClientId = -1;
+            mService.disconnect(clientId, binder);
         } catch (RemoteException e) {
-            Log.w(TAG, "disconnect RemoteException (FYI - ignoring): " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
     @Override
     protected void finalize() throws Throwable {
-        if (mBinder != null) {
-            if (DBG) Log.d(TAG, "finalize: disconnect() not called - executing now");
+        if (mState != STATE_UNCONNECTED) {
             disconnect();
         }
     }
 
     /**
-     * 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 WifiNanEventCallback#onConfigCompleted(ConfigRequest)} will be
-     * called when configuration is completed (if a callback 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);
-
-        if (mClientId == -1) {
-            Log.e(TAG, "requestConfig(): called without an initial connect()!");
-            throw new IllegalStateException("Calling requestConfig() without a connect() first!");
-        }
-
-        try {
-            mService.requestConfig(mClientId, configRequest);
-        } catch (RemoteException e) {
-            Log.w(TAG, "requestConfig RemoteException (FYI - ignoring): " + e);
-        }
-    }
-
-    /**
      * Request a NAN publish session. The actual publish session is provided by
      * the
      * {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)}
@@ -170,30 +194,22 @@ public class WifiNanManager {
     public void publish(PublishConfig publishConfig, WifiNanSessionCallback callback) {
         if (VDBG) Log.v(TAG, "publish(): config=" + publishConfig);
 
-        if (publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED
-                && publishConfig.mRxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish config: UNSOLICITED "
-                    + "publishes (active) can't have an Rx filter");
-        }
-        if (publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_SOLICITED
-                && publishConfig.mTxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish config: SOLICITED "
-                    + "publishes (passive) can't have a Tx filter");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("Invalid callback - must not be null");
-        }
+        int clientId;
+        Looper looper;
+        synchronized (mLock) {
+            if (mState != STATE_CONNECTED) {
+                Log.e(TAG, "publish(): called when not CONNECTED!");
+                return;
+            }
 
-        if (mClientId == -1) {
-            Log.e(TAG, "publish(): called without an initial connect()!");
-            throw new IllegalStateException("Calling publish() without a connect() first!");
+            clientId = mClientId;
+            looper = mLooper;
         }
-
         try {
-            mService.publish(mClientId, publishConfig,
-                    new WifiNanSessionCallbackProxy(mLooper, true, callback));
+            mService.publish(clientId, publishConfig,
+                    new WifiNanSessionCallbackProxy(this, looper, true, callback));
         } catch (RemoteException e) {
-            Log.w(TAG, "publish RemoteException: " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -203,21 +219,19 @@ public class WifiNanManager {
     public void updatePublish(int sessionId, PublishConfig publishConfig) {
         if (VDBG) Log.v(TAG, "updatePublish(): config=" + publishConfig);
 
-        if (publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED
-                && publishConfig.mRxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish config: UNSOLICITED "
-                    + "publishes (active) can't have an Rx filter");
-        }
-        if (publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_SOLICITED
-                && publishConfig.mTxFilterLength != 0) {
-            throw new IllegalArgumentException("Invalid publish config: SOLICITED "
-                    + "publishes (passive) can't have a Tx filter");
-        }
+        int clientId;
+        synchronized (mLock) {
+            if (mState != STATE_CONNECTED) {
+                Log.e(TAG, "updatePublish(): called when not CONNECTED)!");
+                return;
+            }
 
+            clientId = mClientId;
+        }
         try {
-            mService.updatePublish(mClientId, sessionId, publishConfig);
+            mService.updatePublish(clientId, sessionId, publishConfig);
         } catch (RemoteException e) {
-            Log.w(TAG, "updatePublish RemoteException: " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -239,27 +253,23 @@ public class WifiNanManager {
             Log.v(TAG, "subscribe(): config=" + subscribeConfig);
         }
 
-        if (subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE
-                && subscribeConfig.mRxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe config: ACTIVE subscribes can't have an Rx filter");
-        }
-        if (subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE
-                && subscribeConfig.mTxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe config: PASSIVE subscribes can't have a Tx filter");
-        }
+        int clientId;
+        Looper looper;
+        synchronized (mLock) {
+            if (mState != STATE_CONNECTED) {
+                Log.e(TAG, "subscribe(): called when not CONNECTED!");
+                return;
+            }
 
-        if (mClientId == -1) {
-            Log.e(TAG, "subscribe(): called without an initial connect()!");
-            throw new IllegalStateException("Calling subscribe() without a connect() first!");
+            clientId = mClientId;
+            looper = mLooper;
         }
 
         try {
-            mService.subscribe(mClientId, subscribeConfig,
-                    new WifiNanSessionCallbackProxy(mLooper, false, callback));
+            mService.subscribe(clientId, subscribeConfig,
+                    new WifiNanSessionCallbackProxy(this, looper, false, callback));
         } catch (RemoteException e) {
-            Log.w(TAG, "subscribe RemoteException: " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -271,21 +281,20 @@ public class WifiNanManager {
             Log.v(TAG, "subscribe(): config=" + subscribeConfig);
         }
 
-        if (subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE
-                && subscribeConfig.mRxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe config: ACTIVE subscribes can't have an Rx filter");
-        }
-        if (subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE
-                && subscribeConfig.mTxFilterLength != 0) {
-            throw new IllegalArgumentException(
-                    "Invalid subscribe config: PASSIVE subscribes can't have a Tx filter");
+        int clientId;
+        synchronized (mLock) {
+            if (mState != STATE_CONNECTED) {
+                Log.e(TAG, "updateSubscribe(): called when not CONNECTED!");
+                return;
+            }
+
+            clientId = mClientId;
         }
 
         try {
-            mService.updateSubscribe(mClientId, sessionId, subscribeConfig);
+            mService.updateSubscribe(clientId, sessionId, subscribeConfig);
         } catch (RemoteException e) {
-            Log.w(TAG, "updateSubscribe RemoteException: " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -295,10 +304,20 @@ public class WifiNanManager {
     public void terminateSession(int sessionId) {
         if (DBG) Log.d(TAG, "Terminate NAN session #" + sessionId);
 
+        int clientId;
+        synchronized (mLock) {
+            if (mState != STATE_CONNECTED) {
+                Log.e(TAG, "terminateSession(): called when not CONNECTED!");
+                return;
+            }
+
+            clientId = mClientId;
+        }
+
         try {
-            mService.terminateSession(mClientId, sessionId);
+            mService.terminateSession(clientId, sessionId);
         } catch (RemoteException e) {
-            Log.w(TAG, "terminateSession RemoteException (FYI - ignoring): " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -307,24 +326,34 @@ public class WifiNanManager {
      */
     public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
             int messageId) {
-        try {
-            if (VDBG) {
-                Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
-                        + ", messageLength=" + messageLength + ", messageId=" + messageId);
+        if (VDBG) {
+            Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
+                    + ", messageLength=" + messageLength + ", messageId=" + messageId);
+        }
+
+        int clientId;
+        synchronized (mLock) {
+            if (mState != STATE_CONNECTED) {
+                Log.e(TAG, "sendMessage(): called when not CONNECTED!");
+                return;
             }
-            mService.sendMessage(mClientId, sessionId, peerId, message, messageLength, messageId);
+
+            clientId = mClientId;
+        }
+
+        try {
+            mService.sendMessage(clientId, sessionId, peerId, message, messageLength, messageId);
         } catch (RemoteException e) {
-            Log.w(TAG, "subscribe RemoteException (FYI - ignoring): " + e);
+            e.rethrowAsRuntimeException();
         }
     }
 
     private static class WifiNanEventCallbackProxy extends IWifiNanEventCallback.Stub {
-        private static final int CALLBACK_CONFIG_COMPLETED = 0;
-        private static final int CALLBACK_CONFIG_FAILED = 1;
+        private static final int CALLBACK_CONNECT_SUCCESS = 0;
+        private static final int CALLBACK_CONNECT_FAIL = 1;
         private static final int CALLBACK_NAN_DOWN = 2;
         private static final int CALLBACK_IDENTITY_CHANGED = 3;
 
-        private final WifiNanEventCallback mOriginalCallback;
         private final Handler mHandler;
 
         /**
@@ -333,26 +362,64 @@ public class WifiNanManager {
          *
          * @param looper The looper on which to execute the callbacks.
          */
-        WifiNanEventCallbackProxy(Looper looper, WifiNanEventCallback originalCallback) {
-            mOriginalCallback = originalCallback;
+        WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper,
+                final WifiNanEventCallback originalCallback) {
+            final WeakReference<WifiNanManager> nanManager = new WeakReference<WifiNanManager>(mgr);
 
             if (VDBG) Log.v(TAG, "WifiNanEventCallbackProxy ctor: looper=" + looper);
             mHandler = new Handler(looper) {
                 @Override
                 public void handleMessage(Message msg) {
-                    if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+                    if (DBG) {
+                        Log.d(TAG, "WifiNanEventCallbackProxy: What=" + msg.what + ", msg=" + msg);
+                    }
+
+                    WifiNanManager mgr = nanManager.get();
+                    if (mgr == null) {
+                        Log.w(TAG, "WifiNanEventCallbackProxy: handleMessage post GC");
+                        return;
+                    }
+
                     switch (msg.what) {
-                        case CALLBACK_CONFIG_COMPLETED:
-                            mOriginalCallback.onConfigCompleted((ConfigRequest) msg.obj);
+                        case CALLBACK_CONNECT_SUCCESS:
+                            synchronized (mgr.mLock) {
+                                if (mgr.mState != STATE_CONNECTING) {
+                                    Log.w(TAG, "onConnectSuccess indication received but not in "
+                                            + "CONNECTING state. Ignoring.");
+                                    return;
+                                }
+                                mgr.mState = STATE_CONNECTED;
+                            }
+                            originalCallback.onConnectSuccess();
                             break;
-                        case CALLBACK_CONFIG_FAILED:
-                            mOriginalCallback.onConfigFailed((ConfigRequest) msg.obj, msg.arg1);
+                        case CALLBACK_CONNECT_FAIL:
+                            synchronized (mgr.mLock) {
+                                if (mgr.mState != STATE_CONNECTING) {
+                                    Log.w(TAG, "onConnectFail indication received but not in "
+                                            + "CONNECTING state. Ignoring.");
+                                    return;
+                                }
+
+                                mgr.mState = STATE_UNCONNECTED;
+                                mgr.mBinder = null;
+                                mgr.mLooper = null;
+                                mgr.mClientId = 0;
+                            }
+                            nanManager.clear();
+                            originalCallback.onConnectFail(msg.arg1);
                             break;
                         case CALLBACK_NAN_DOWN:
-                            mOriginalCallback.onNanDown(msg.arg1);
+                            synchronized (mgr.mLock) {
+                                mgr.mState = STATE_UNCONNECTED;
+                                mgr.mBinder = null;
+                                mgr.mLooper = null;
+                                mgr.mClientId = 0;
+                            }
+                            nanManager.clear();
+                            originalCallback.onNanDown(msg.arg1);
                             break;
                         case CALLBACK_IDENTITY_CHANGED:
-                            mOriginalCallback.onIdentityChanged();
+                            originalCallback.onIdentityChanged();
                             break;
                     }
                 }
@@ -360,24 +427,19 @@ public class WifiNanManager {
         }
 
         @Override
-        public void onConfigCompleted(ConfigRequest completedConfig) {
-            if (VDBG) Log.v(TAG, "onConfigCompleted: configRequest=" + completedConfig);
+        public void onConnectSuccess() {
+            if (VDBG) Log.v(TAG, "onConnectSuccess");
 
-            Message msg = mHandler.obtainMessage(CALLBACK_CONFIG_COMPLETED);
-            msg.obj = completedConfig;
+            Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_SUCCESS);
             mHandler.sendMessage(msg);
         }
 
         @Override
-        public void onConfigFailed(ConfigRequest failedConfig, int reason) {
-            if (VDBG) {
-                Log.v(TAG,
-                        "onConfigFailed: failedConfig=" + failedConfig + ", reason=" + reason);
-            }
+        public void onConnectFail(int reason) {
+            if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason);
 
-            Message msg = mHandler.obtainMessage(CALLBACK_CONFIG_FAILED);
+            Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_FAIL);
             msg.arg1 = reason;
-            msg.obj = failedConfig;
             mHandler.sendMessage(msg);
         }
 
@@ -399,27 +461,30 @@ public class WifiNanManager {
         }
     }
 
-    private class WifiNanSessionCallbackProxy extends IWifiNanSessionCallback.Stub {
+    private static class WifiNanSessionCallbackProxy extends IWifiNanSessionCallback.Stub {
         private static final int CALLBACK_SESSION_STARTED = 0;
-        private static final int CALLBACK_SESSION_CONFIG_FAIL = 1;
-        private static final int CALLBACK_SESSION_TERMINATED = 2;
-        private static final int CALLBACK_MATCH = 3;
-        private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 4;
-        private static final int CALLBACK_MESSAGE_SEND_FAIL = 5;
-        private static final int CALLBACK_MESSAGE_RECEIVED = 6;
+        private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1;
+        private static final int CALLBACK_SESSION_CONFIG_FAIL = 2;
+        private static final int CALLBACK_SESSION_TERMINATED = 3;
+        private static final int CALLBACK_MATCH = 4;
+        private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5;
+        private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
+        private static final int CALLBACK_MESSAGE_RECEIVED = 7;
 
         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 WeakReference<WifiNanManager> mNanManager;
         private final boolean mIsPublish;
         private final WifiNanSessionCallback mOriginalCallback;
 
         private final Handler mHandler;
         private WifiNanSession mSession;
 
-        WifiNanSessionCallbackProxy(Looper looper, boolean isPublish,
+        WifiNanSessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish,
                 WifiNanSessionCallback originalCallback) {
+            mNanManager = new WeakReference<>(mgr);
             mIsPublish = isPublish;
             mOriginalCallback = originalCallback;
 
@@ -429,12 +494,28 @@ public class WifiNanManager {
                 @Override
                 public void handleMessage(Message msg) {
                     if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+
+                    if (mNanManager.get() == null) {
+                        Log.w(TAG, "WifiNanSessionCallbackProxy: handleMessage post GC");
+                        return;
+                    }
+
                     switch (msg.what) {
                         case CALLBACK_SESSION_STARTED:
                             onProxySessionStarted(msg.arg1);
                             break;
+                        case CALLBACK_SESSION_CONFIG_SUCCESS:
+                            mOriginalCallback.onSessionConfigSuccess();
+                            break;
                         case CALLBACK_SESSION_CONFIG_FAIL:
-                            onProxySessionConfigFail(msg.arg1);
+                            mOriginalCallback.onSessionConfigFail(msg.arg1);
+                            if (mSession == null) {
+                                /*
+                                 * creation failed (as opposed to update
+                                 * failing)
+                                 */
+                                mNanManager.clear();
+                            }
                             break;
                         case CALLBACK_SESSION_TERMINATED:
                             onProxySessionTerminated(msg.arg1);
@@ -473,6 +554,14 @@ public class WifiNanManager {
         }
 
         @Override
+        public void onSessionConfigSuccess() {
+            if (VDBG) Log.v(TAG, "onSessionConfigSuccess");
+
+            Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_SUCCESS);
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
         public void onSessionConfigFail(int reason) {
             if (VDBG) Log.v(TAG, "onSessionConfigFail: reason=" + reason);
 
@@ -554,32 +643,34 @@ public class WifiNanManager {
                 throw new IllegalStateException(
                         "onSessionStarted: sessionId=" + sessionId + ": session already created!?");
             }
+
+            WifiNanManager mgr = mNanManager.get();
+            if (mgr == null) {
+                Log.w(TAG, "onProxySessionStarted: mgr GC'd");
+                return;
+            }
+
             if (mIsPublish) {
-                WifiNanPublishSession session = new WifiNanPublishSession(WifiNanManager.this,
-                        sessionId, mOriginalCallback);
+                WifiNanPublishSession session = new WifiNanPublishSession(mgr, sessionId);
                 mSession = session;
                 mOriginalCallback.onPublishStarted(session);
             } else {
-                WifiNanSubscribeSession session = new WifiNanSubscribeSession(WifiNanManager.this,
-                        sessionId, mOriginalCallback);
+                WifiNanSubscribeSession session = new WifiNanSubscribeSession(mgr, sessionId);
                 mSession = session;
                 mOriginalCallback.onSubscribeStarted(session);
             }
         }
 
-        public void onProxySessionConfigFail(int reason) {
-            if (VDBG) Log.v(TAG, "Proxy: onSessionConfigFail: reason=" + reason);
-            mOriginalCallback.onSessionConfigFail(reason);
-        }
-
         public void onProxySessionTerminated(int reason) {
             if (VDBG) Log.v(TAG, "Proxy: onSessionTerminated: reason=" + reason);
-            mOriginalCallback.onSessionTerminated(reason);
             if (mSession != null) {
-                mSession.terminate();
+                mSession.setTerminated();
+                mSession = null;
             } else {
                 Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?");
             }
+            mNanManager.clear();
+            mOriginalCallback.onSessionTerminated(reason);
         }
     }
 }
index 3b1c094..27e4a54 100644 (file)
@@ -16,6 +16,8 @@
 
 package android.net.wifi.nan;
 
+import android.util.Log;
+
 /**
  * A representation of a NAN publish session. Created when
  * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} is
@@ -25,12 +27,13 @@ package android.net.wifi.nan;
  * @hide PROPOSED_NAN_API
  */
 public class WifiNanPublishSession extends WifiNanSession {
+    private static final String TAG = "WifiNanPublishSession";
+
     /**
      * {@hide}
      */
-    public WifiNanPublishSession(WifiNanManager manager, int sessionId,
-            WifiNanSessionCallback callback) {
-        super(manager, sessionId, callback);
+    public WifiNanPublishSession(WifiNanManager manager, int sessionId) {
+        super(manager, sessionId);
     }
 
     /**
@@ -43,10 +46,16 @@ public class WifiNanPublishSession extends WifiNanSession {
      */
     public void updatePublish(PublishConfig publishConfig) {
         if (mTerminated) {
-            mCallback.onSessionConfigFail(WifiNanSessionCallback.FAIL_REASON_SESSION_TERMINATED);
+            Log.w(TAG, "updatePublish: called on terminated session");
             return;
         } else {
-            mManager.updatePublish(mSessionId, publishConfig);
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "updatePublish: called post GC on WifiNanManager");
+                return;
+            }
+
+            mgr.updatePublish(mSessionId, publishConfig);
         }
     }
 }
index 50df0aa..890f757 100644 (file)
@@ -18,6 +18,8 @@ package android.net.wifi.nan;
 
 import android.util.Log;
 
+import java.lang.ref.WeakReference;
+
 /**
  * A representation of a single publish or subscribe NAN session. This object
  * will not be created directly - only its child classes are available:
@@ -30,22 +32,29 @@ public class WifiNanSession {
     private static final boolean DBG = false;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    protected WifiNanManager mManager;
+    /**
+     * @hide
+     */
+    protected WeakReference<WifiNanManager> mMgr;
+
+    /**
+     * @hide
+     */
     protected final int mSessionId;
-    protected final WifiNanSessionCallback mCallback;
 
+    /**
+     * @hide
+     */
     protected boolean mTerminated = false;
 
     /**
      * {@hide}
      */
-    public WifiNanSession(WifiNanManager manager, int sessionId,
-            WifiNanSessionCallback callback) {
+    public WifiNanSession(WifiNanManager manager, int sessionId) {
         if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId);
 
-        mManager = manager;
+        mMgr = new WeakReference<>(manager);
         mSessionId = sessionId;
-        mCallback = callback;
     }
 
     /**
@@ -55,9 +64,29 @@ public class WifiNanSession {
      * additional operations are termination.
      */
     public void terminate() {
-        mManager.terminateSession(mSessionId);
+        WifiNanManager mgr = mMgr.get();
+        if (mgr == null) {
+            Log.w(TAG, "terminate: called post GC on WifiNanManager");
+            return;
+        }
+        mgr.terminateSession(mSessionId);
         mTerminated = true;
-        mManager = null;
+        mMgr.clear();
+    }
+
+    /**
+     * Sets the status of the session to terminated - i.e. an indication that
+     * already terminated rather than executing a termination.
+     *
+     * @hide
+     */
+    public void setTerminated() {
+        if (mTerminated) {
+            Log.w(TAG, "terminate: already terminated.");
+            return;
+        }
+        mTerminated = true;
+        mMgr.clear();
     }
 
     @Override
@@ -66,8 +95,8 @@ public class WifiNanSession {
             Log.w(TAG, "WifiNanSession mSessionId=" + mSessionId
                     + " was not explicitly terminated. The session may use resources until "
                     + "terminated so step should be done explicitly");
+            terminate();
         }
-        terminate();
     }
 
     /**
@@ -89,11 +118,16 @@ public class WifiNanSession {
      */
     public void sendMessage(int peerId, byte[] message, int messageLength, int messageId) {
         if (mTerminated) {
-            mCallback.onMessageSendFail(messageId,
-                    WifiNanSessionCallback.FAIL_REASON_SESSION_TERMINATED);
+            Log.w(TAG, "sendMessage: called on terminated session");
             return;
-        }
+        } else {
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "sendMessage: called post GC on WifiNanManager");
+                return;
+            }
 
-        mManager.sendMessage(mSessionId, peerId, message, messageLength, messageId);
+            mgr.sendMessage(mSessionId, peerId, message, messageLength, messageId);
+        }
     }
 }
index 543836e..baa22cc 100644 (file)
 
 package android.net.wifi.nan;
 
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * 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(PublishConfig, WifiNanSessionCallback)} or
  * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} .
- * These are callbacks applying to a specific NAN session. Events corresponding
- * to the NAN link are delivered using {@link WifiNanEventCallback}.
+ * These are callbacks applying to a specific NAN session.
  * <p>
  * A single callback is registered at session creation - it cannot be replaced.
  *
  * @hide PROPOSED_NAN_API
  */
 public class WifiNanSessionCallback {
+    @IntDef({
+            REASON_NO_RESOURCES, REASON_INVALID_ARGS, REASON_NO_MATCH_SESSION,
+            REASON_TX_FAIL, REASON_OTHER })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionReasonCodes {
+    }
+
+    @IntDef({
+            TERMINATE_REASON_DONE, TERMINATE_REASON_FAIL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionTerminateCodes {
+    }
+
     /**
-     * Failure reason flag for {@link WifiNanEventCallback} and
-     * {@link WifiNanSessionCallback} callbacks. Indicates no resources to
-     * execute the requested operation.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     * Indicates no resources to execute the requested operation.
      */
-    public static final int FAIL_REASON_NO_RESOURCES = 0;
+    public static final int REASON_NO_RESOURCES = 0;
 
     /**
-     * Failure reason flag for {@link WifiNanEventCallback} and
-     * {@link WifiNanSessionCallback} callbacks. Indicates invalid argument in
-     * the requested operation.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     * Indicates invalid argument in the requested operation.
      */
-    public static final int FAIL_REASON_INVALID_ARGS = 1;
+    public static final int REASON_INVALID_ARGS = 1;
 
     /**
-     * Failure reason flag for {@link WifiNanEventCallback} and
-     * {@link WifiNanSessionCallback} callbacks. Indicates a message is
-     * transmitted without a match (i.e. a discovery) occurring first.
+     * Failure reason flag for {@link WifiNanSessionCallback} 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;
+    public static final int REASON_NO_MATCH_SESSION = 2;
 
     /**
-     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
-     * Indicates that a command has been issued to a session which is
-     * terminated. Session termination may have been caused explicitly by the
-     * user using the {@code WifiNanSession#terminate()} or implicitly as a
-     * result of the original session reaching its lifetime or being terminated
-     * due to an error.
+     * Failure reason flag for
+     * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} callback.
+     * Indicates transmission failure: this may be due to local transmission
+     * failure or to no ACK received - i.e. remote device didn't receive the
+     * sent message.
      */
-    public static final int FAIL_REASON_SESSION_TERMINATED = 3;
+    public static final int REASON_TX_FAIL = 3;
 
     /**
-     * Failure reason flag for {@link WifiNanEventCallback} and
-     * {@link WifiNanSessionCallback} callbacks. Indicates an unspecified error
-     * occurred during the operation.
+     * Failure reason flag for {@link WifiNanSessionCallback} callbacks.
+     * Indicates an unspecified error occurred during the operation.
      */
-    public static final int FAIL_REASON_OTHER = 4;
+    public static final int REASON_OTHER = 4;
 
     /**
      * Failure reason flag for
@@ -75,7 +88,7 @@ public class WifiNanSessionCallback {
      * requested operations (per {@link PublishConfig} or
      * {@link SubscribeConfig}) have been executed.
      */
-    public static final int TERMINATE_REASON_DONE = 0;
+    public static final int TERMINATE_REASON_DONE = 100;
 
     /**
      * Failure reason flag for
@@ -83,7 +96,7 @@ public class WifiNanSessionCallback {
      * Indicates that publish or subscribe session is terminated due to a
      * failure.
      */
-    public static final int TERMINATE_REASON_FAIL = 1;
+    public static final int TERMINATE_REASON_FAIL = 101;
 
     /**
      * Called when a publish operation is started successfully.
@@ -105,15 +118,22 @@ public class WifiNanSessionCallback {
         /* empty */
     }
 
+    /**
+     * Called when a session update configuration (publish or subscribe update)
+     * succeeds.
+     */
+    public void onSessionConfigSuccess() {
+        /* empty */
+    }
 
     /**
      * Called when a session configuration (publish or subscribe setup or
      * update) fails.
      *
      * @param reason The failure reason using
-     *            {@code WifiNanSessionCallback.FAIL_*} codes.
+     *            {@code WifiNanSessionCallback.REASON_*} codes.
      */
-    public void onSessionConfigFail(int reason) {
+    public void onSessionConfigFail(@SessionReasonCodes int reason) {
         /* empty */
     }
 
@@ -123,7 +143,7 @@ public class WifiNanSessionCallback {
      * @param reason The termination reason using
      *            {@code WifiNanSessionCallback.TERMINATE_*} codes.
      */
-    public void onSessionTerminated(int reason) {
+    public void onSessionTerminated(@SessionTerminateCodes int reason) {
         /* empty */
     }
 
@@ -171,9 +191,10 @@ public class WifiNanSessionCallback {
      * - never both
      *
      * @param reason The failure reason using
-     *            {@code WifiNanSessionCallback.FAIL_*} codes.
+     *            {@code WifiNanSessionCallback.REASON_*} codes.
      */
-    public void onMessageSendFail(@SuppressWarnings("unused") int messageId, int reason) {
+    public void onMessageSendFail(@SuppressWarnings("unused") int messageId,
+            @SessionReasonCodes int reason) {
         /* empty */
     }
 
index 8151fd6..0b2d231 100644 (file)
@@ -16,6 +16,8 @@
 
 package android.net.wifi.nan;
 
+import android.util.Log;
+
 /**
  * A representation of a NAN subscribe session. Created when
  * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} is
@@ -25,12 +27,13 @@ package android.net.wifi.nan;
  * @hide PROPOSED_NAN_API
  */
 public class WifiNanSubscribeSession extends WifiNanSession {
+    private static final String TAG = "WifiNanSubscribeSession";
+
     /**
      * {@hide}
      */
-    public WifiNanSubscribeSession(WifiNanManager manager, int sessionId,
-            WifiNanSessionCallback callback) {
-        super(manager, sessionId, callback);
+    public WifiNanSubscribeSession(WifiNanManager manager, int sessionId) {
+        super(manager, sessionId);
     }
 
     /**
@@ -43,10 +46,16 @@ public class WifiNanSubscribeSession extends WifiNanSession {
      */
     public void updateSubscribe(SubscribeConfig subscribeConfig) {
         if (mTerminated) {
-            mCallback.onSessionConfigFail(WifiNanSessionCallback.FAIL_REASON_SESSION_TERMINATED);
+            Log.w(TAG, "updateSubscribe: called on terminated session");
             return;
         } else {
-            mManager.updateSubscribe(mSessionId, subscribeConfig);
+            WifiNanManager mgr = mMgr.get();
+            if (mgr == null) {
+                Log.w(TAG, "updateSubscribe: called post GC on WifiNanManager");
+                return;
+            }
+
+            mgr.updateSubscribe(mSessionId, subscribeConfig);
         }
     }
 }