1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi.aware;
18 
19 import android.hardware.wifi.V1_0.NanStatusType;
20 import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
21 import android.net.wifi.aware.PublishConfig;
22 import android.net.wifi.aware.SubscribeConfig;
23 import android.os.RemoteException;
24 import android.util.Log;
25 import android.util.SparseArray;
26 
27 import libcore.util.HexEncoding;
28 
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 import java.util.Arrays;
32 
33 /**
34  * Manages the state of a single Aware discovery session (publish or subscribe).
35  * Primary state consists of a callback through which session callbacks are
36  * executed as well as state related to currently active discovery sessions:
37  * publish/subscribe ID, and MAC address caching (hiding) from clients.
38  */
39 public class WifiAwareDiscoverySessionState {
40     private static final String TAG = "WifiAwareDiscSessState";
41     private static final boolean VDBG = false; // STOPSHIP if true
42     /* package */ boolean mDbg = false;
43 
44     private static int sNextPeerIdToBeAllocated = 100; // used to create a unique peer ID
45 
46     private final WifiAwareNativeApi mWifiAwareNativeApi;
47     private int mSessionId;
48     private byte mPubSubId;
49     private IWifiAwareDiscoverySessionCallback mCallback;
50     private boolean mIsPublishSession;
51     private boolean mIsRangingEnabled;
52     private final long mCreationTime;
53 
54     static class PeerInfo {
PeerInfo(int instanceId, byte[] mac)55         PeerInfo(int instanceId, byte[] mac) {
56             mInstanceId = instanceId;
57             mMac = mac;
58         }
59 
60         int mInstanceId;
61         byte[] mMac;
62 
63         @Override
toString()64         public String toString() {
65             StringBuilder sb = new StringBuilder("instanceId [");
66             sb.append(mInstanceId).append(", mac=").append(HexEncoding.encode(mMac)).append("]");
67             return sb.toString();
68         }
69     }
70 
71     private final SparseArray<PeerInfo> mPeerInfoByRequestorInstanceId = new SparseArray<>();
72 
WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId, byte pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession, boolean isRangingEnabled, long creationTime)73     public WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId,
74             byte pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession,
75             boolean isRangingEnabled, long creationTime) {
76         mWifiAwareNativeApi = wifiAwareNativeApi;
77         mSessionId = sessionId;
78         mPubSubId = pubSubId;
79         mCallback = callback;
80         mIsPublishSession = isPublishSession;
81         mIsRangingEnabled = isRangingEnabled;
82         mCreationTime = creationTime;
83     }
84 
getSessionId()85     public int getSessionId() {
86         return mSessionId;
87     }
88 
getPubSubId()89     public int getPubSubId() {
90         return mPubSubId;
91     }
92 
isPublishSession()93     public boolean isPublishSession() {
94         return mIsPublishSession;
95     }
96 
isRangingEnabled()97     public boolean isRangingEnabled() {
98         return mIsRangingEnabled;
99     }
100 
getCreationTime()101     public long getCreationTime() {
102         return mCreationTime;
103     }
104 
getCallback()105     public IWifiAwareDiscoverySessionCallback getCallback() {
106         return mCallback;
107     }
108 
109     /**
110      * Return the peer information of the specified peer ID - or a null if no such peer ID is
111      * registered.
112      */
getPeerInfo(int peerId)113     public PeerInfo getPeerInfo(int peerId) {
114         return mPeerInfoByRequestorInstanceId.get(peerId);
115     }
116 
117     /**
118      * Destroy the current discovery session - stops publishing or subscribing
119      * if currently active.
120      */
terminate()121     public void terminate() {
122         mCallback = null;
123 
124         if (mIsPublishSession) {
125             mWifiAwareNativeApi.stopPublish((short) 0, mPubSubId);
126         } else {
127             mWifiAwareNativeApi.stopSubscribe((short) 0, mPubSubId);
128         }
129     }
130 
131     /**
132      * Indicates whether the publish/subscribe ID (a HAL ID) corresponds to this
133      * session.
134      *
135      * @param pubSubId The publish/subscribe HAL ID to be tested.
136      * @return true if corresponds to this session, false otherwise.
137      */
isPubSubIdSession(int pubSubId)138     public boolean isPubSubIdSession(int pubSubId) {
139         return mPubSubId == pubSubId;
140     }
141 
142     /**
143      * Modify a publish discovery session.
144      *
145      * @param transactionId Transaction ID for the transaction - used in the
146      *            async callback to match with the original request.
147      * @param config Configuration of the publish session.
148      */
updatePublish(short transactionId, PublishConfig config)149     public boolean updatePublish(short transactionId, PublishConfig config) {
150         if (!mIsPublishSession) {
151             Log.e(TAG, "A SUBSCRIBE session is being used to publish");
152             try {
153                 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
154             } catch (RemoteException e) {
155                 Log.e(TAG, "updatePublish: RemoteException=" + e);
156             }
157             return false;
158         }
159 
160         boolean success = mWifiAwareNativeApi.publish(transactionId, mPubSubId, config);
161         if (!success) {
162             try {
163                 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
164             } catch (RemoteException e) {
165                 Log.w(TAG, "updatePublish onSessionConfigFail(): RemoteException (FYI): " + e);
166             }
167         }
168 
169         return success;
170     }
171 
172     /**
173      * Modify a subscribe discovery session.
174      *
175      * @param transactionId Transaction ID for the transaction - used in the
176      *            async callback to match with the original request.
177      * @param config Configuration of the subscribe session.
178      */
updateSubscribe(short transactionId, SubscribeConfig config)179     public boolean updateSubscribe(short transactionId, SubscribeConfig config) {
180         if (mIsPublishSession) {
181             Log.e(TAG, "A PUBLISH session is being used to subscribe");
182             try {
183                 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
184             } catch (RemoteException e) {
185                 Log.e(TAG, "updateSubscribe: RemoteException=" + e);
186             }
187             return false;
188         }
189 
190         boolean success = mWifiAwareNativeApi.subscribe(transactionId, mPubSubId, config);
191         if (!success) {
192             try {
193                 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
194             } catch (RemoteException e) {
195                 Log.w(TAG, "updateSubscribe onSessionConfigFail(): RemoteException (FYI): " + e);
196             }
197         }
198 
199         return success;
200     }
201 
202     /**
203      * Send a message to a peer which is part of a discovery session.
204      *
205      * @param transactionId Transaction ID for the transaction - used in the
206      *            async callback to match with the original request.
207      * @param peerId ID of the peer. Obtained through previous communication (a
208      *            match indication).
209      * @param message Message byte array to send to the peer.
210      * @param messageId A message ID provided by caller to be used in any
211      *            callbacks related to the message (success/failure).
212      */
sendMessage(short transactionId, int peerId, byte[] message, int messageId)213     public boolean sendMessage(short transactionId, int peerId, byte[] message, int messageId) {
214         PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.get(peerId);
215         if (peerInfo == null) {
216             Log.e(TAG, "sendMessage: attempting to send a message to an address which didn't "
217                     + "match/contact us");
218             try {
219                 mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE);
220             } catch (RemoteException e) {
221                 Log.e(TAG, "sendMessage: RemoteException=" + e);
222             }
223             return false;
224         }
225 
226         boolean success = mWifiAwareNativeApi.sendMessage(transactionId, mPubSubId,
227                 peerInfo.mInstanceId, peerInfo.mMac, message, messageId);
228         if (!success) {
229             try {
230                 mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE);
231             } catch (RemoteException e) {
232                 Log.e(TAG, "sendMessage: RemoteException=" + e);
233             }
234             return false;
235         }
236 
237         return success;
238     }
239 
240     /**
241      * Callback from HAL when a discovery occurs - i.e. when a match to an
242      * active subscription request or to a solicited publish request occurs.
243      * Propagates to client if registered.
244      *
245      * @param requestorInstanceId The ID used to identify the peer in this
246      *            matched session.
247      * @param peerMac The MAC address of the peer. Never propagated to client
248      *            due to privacy concerns.
249      * @param serviceSpecificInfo Information from the discovery advertisement
250      *            (usually not used in the match decisions).
251      * @param matchFilter The filter from the discovery advertisement (which was
252      *            used in the match decision).
253      * @param rangingIndication Bit mask indicating the type of ranging event triggered.
254      * @param rangeMm The range to the peer in mm (valid if rangingIndication specifies ingress
255      *                or egress events - i.e. non-zero).
256      */
onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndication, int rangeMm)257     public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo,
258             byte[] matchFilter, int rangingIndication, int rangeMm) {
259         int peerId = getPeerIdOrAddIfNew(requestorInstanceId, peerMac);
260 
261         try {
262             if (rangingIndication == 0) {
263                 mCallback.onMatch(peerId, serviceSpecificInfo, matchFilter);
264             } else {
265                 mCallback.onMatchWithDistance(peerId, serviceSpecificInfo, matchFilter, rangeMm);
266             }
267         } catch (RemoteException e) {
268             Log.w(TAG, "onMatch: RemoteException (FYI): " + e);
269         }
270     }
271 
272     /**
273      * Callback from HAL when a message is received from a peer in a discovery
274      * session. Propagated to client if registered.
275      *
276      * @param requestorInstanceId An ID used to identify the peer.
277      * @param peerMac The MAC address of the peer sending the message. This
278      *            information is never propagated to the client due to privacy
279      *            concerns.
280      * @param message The received message.
281      */
onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message)282     public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message) {
283         int peerId = getPeerIdOrAddIfNew(requestorInstanceId, peerMac);
284 
285         try {
286             mCallback.onMessageReceived(peerId, message);
287         } catch (RemoteException e) {
288             Log.w(TAG, "onMessageReceived: RemoteException (FYI): " + e);
289         }
290     }
291 
getPeerIdOrAddIfNew(int requestorInstanceId, byte[] peerMac)292     private int getPeerIdOrAddIfNew(int requestorInstanceId, byte[] peerMac) {
293         for (int i = 0; i < mPeerInfoByRequestorInstanceId.size(); ++i) {
294             PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.valueAt(i);
295             if (peerInfo.mInstanceId == requestorInstanceId && Arrays.equals(peerMac,
296                     peerInfo.mMac)) {
297                 return mPeerInfoByRequestorInstanceId.keyAt(i);
298             }
299         }
300 
301         int newPeerId = sNextPeerIdToBeAllocated++;
302         PeerInfo newPeerInfo = new PeerInfo(requestorInstanceId, peerMac);
303         mPeerInfoByRequestorInstanceId.put(newPeerId, newPeerInfo);
304 
305         if (VDBG) {
306             Log.v(TAG, "New peer info: peerId=" + newPeerId + ", peerInfo=" + newPeerInfo);
307         }
308 
309         return newPeerId;
310     }
311 
312     /**
313      * Dump the internal state of the class.
314      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)315     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
316         pw.println("AwareSessionState:");
317         pw.println("  mSessionId: " + mSessionId);
318         pw.println("  mIsPublishSession: " + mIsPublishSession);
319         pw.println("  mPubSubId: " + mPubSubId);
320         pw.println("  mPeerInfoByRequestorInstanceId: [" + mPeerInfoByRequestorInstanceId + "]");
321     }
322 }
323