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.net.wifi.util.HexEncoding; 24 import android.os.RemoteException; 25 import android.util.Log; 26 import android.util.SparseArray; 27 28 import java.io.FileDescriptor; 29 import java.io.PrintWriter; 30 import java.util.Arrays; 31 32 /** 33 * Manages the state of a single Aware discovery session (publish or subscribe). 34 * Primary state consists of a callback through which session callbacks are 35 * executed as well as state related to currently active discovery sessions: 36 * publish/subscribe ID, and MAC address caching (hiding) from clients. 37 */ 38 public class WifiAwareDiscoverySessionState { 39 private static final String TAG = "WifiAwareDiscSessState"; 40 private boolean mDbg = false; 41 42 private static int sNextPeerIdToBeAllocated = 100; // used to create a unique peer ID 43 44 private final WifiAwareNativeApi mWifiAwareNativeApi; 45 private int mSessionId; 46 private byte mPubSubId; 47 private IWifiAwareDiscoverySessionCallback mCallback; 48 private boolean mIsPublishSession; 49 private boolean mIsRangingEnabled; 50 private final long mCreationTime; 51 52 static class PeerInfo { PeerInfo(int instanceId, byte[] mac)53 PeerInfo(int instanceId, byte[] mac) { 54 mInstanceId = instanceId; 55 mMac = mac; 56 } 57 58 int mInstanceId; 59 byte[] mMac; 60 61 @Override toString()62 public String toString() { 63 StringBuilder sb = new StringBuilder("instanceId ["); 64 sb.append(mInstanceId).append(", mac=").append(HexEncoding.encode(mMac)).append("]"); 65 return sb.toString(); 66 } 67 } 68 69 private final SparseArray<PeerInfo> mPeerInfoByRequestorInstanceId = new SparseArray<>(); 70 WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId, byte pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession, boolean isRangingEnabled, long creationTime)71 public WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId, 72 byte pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession, 73 boolean isRangingEnabled, long creationTime) { 74 mWifiAwareNativeApi = wifiAwareNativeApi; 75 mSessionId = sessionId; 76 mPubSubId = pubSubId; 77 mCallback = callback; 78 mIsPublishSession = isPublishSession; 79 mIsRangingEnabled = isRangingEnabled; 80 mCreationTime = creationTime; 81 } 82 83 /** 84 * Enable verbose logging. 85 */ enableVerboseLogging(boolean verbose)86 public void enableVerboseLogging(boolean verbose) { 87 mDbg = verbose; 88 } 89 getSessionId()90 public int getSessionId() { 91 return mSessionId; 92 } 93 getPubSubId()94 public int getPubSubId() { 95 return mPubSubId; 96 } 97 isPublishSession()98 public boolean isPublishSession() { 99 return mIsPublishSession; 100 } 101 isRangingEnabled()102 public boolean isRangingEnabled() { 103 return mIsRangingEnabled; 104 } 105 setRangingEnabled(boolean enabled)106 public void setRangingEnabled(boolean enabled) { 107 mIsRangingEnabled = enabled; 108 } 109 getCreationTime()110 public long getCreationTime() { 111 return mCreationTime; 112 } 113 getCallback()114 public IWifiAwareDiscoverySessionCallback getCallback() { 115 return mCallback; 116 } 117 118 /** 119 * Return the peer information of the specified peer ID - or a null if no such peer ID is 120 * registered. 121 */ getPeerInfo(int peerId)122 public PeerInfo getPeerInfo(int peerId) { 123 return mPeerInfoByRequestorInstanceId.get(peerId); 124 } 125 126 /** 127 * Destroy the current discovery session - stops publishing or subscribing 128 * if currently active. 129 */ terminate()130 public void terminate() { 131 try { 132 mCallback.onSessionTerminated(NanStatusType.SUCCESS); 133 } catch (RemoteException e) { 134 Log.w(TAG, 135 "onSessionTerminatedLocal onSessionTerminated(): RemoteException (FYI): " + e); 136 } 137 mCallback = null; 138 139 if (mIsPublishSession) { 140 mWifiAwareNativeApi.stopPublish((short) 0, mPubSubId); 141 } else { 142 mWifiAwareNativeApi.stopSubscribe((short) 0, mPubSubId); 143 } 144 } 145 146 /** 147 * Indicates whether the publish/subscribe ID (a HAL ID) corresponds to this 148 * session. 149 * 150 * @param pubSubId The publish/subscribe HAL ID to be tested. 151 * @return true if corresponds to this session, false otherwise. 152 */ isPubSubIdSession(int pubSubId)153 public boolean isPubSubIdSession(int pubSubId) { 154 return mPubSubId == pubSubId; 155 } 156 157 /** 158 * Modify a publish discovery session. 159 * 160 * @param transactionId Transaction ID for the transaction - used in the 161 * async callback to match with the original request. 162 * @param config Configuration of the publish session. 163 */ updatePublish(short transactionId, PublishConfig config)164 public boolean updatePublish(short transactionId, PublishConfig config) { 165 if (!mIsPublishSession) { 166 Log.e(TAG, "A SUBSCRIBE session is being used to publish"); 167 try { 168 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE); 169 } catch (RemoteException e) { 170 Log.e(TAG, "updatePublish: RemoteException=" + e); 171 } 172 return false; 173 } 174 175 boolean success = mWifiAwareNativeApi.publish(transactionId, mPubSubId, config); 176 if (!success) { 177 try { 178 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE); 179 } catch (RemoteException e) { 180 Log.w(TAG, "updatePublish onSessionConfigFail(): RemoteException (FYI): " + e); 181 } 182 } 183 184 return success; 185 } 186 187 /** 188 * Modify a subscribe discovery session. 189 * 190 * @param transactionId Transaction ID for the transaction - used in the 191 * async callback to match with the original request. 192 * @param config Configuration of the subscribe session. 193 */ updateSubscribe(short transactionId, SubscribeConfig config)194 public boolean updateSubscribe(short transactionId, SubscribeConfig config) { 195 if (mIsPublishSession) { 196 Log.e(TAG, "A PUBLISH session is being used to subscribe"); 197 try { 198 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE); 199 } catch (RemoteException e) { 200 Log.e(TAG, "updateSubscribe: RemoteException=" + e); 201 } 202 return false; 203 } 204 205 boolean success = mWifiAwareNativeApi.subscribe(transactionId, mPubSubId, config); 206 if (!success) { 207 try { 208 mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE); 209 } catch (RemoteException e) { 210 Log.w(TAG, "updateSubscribe onSessionConfigFail(): RemoteException (FYI): " + e); 211 } 212 } 213 214 return success; 215 } 216 217 /** 218 * Send a message to a peer which is part of a discovery session. 219 * 220 * @param transactionId Transaction ID for the transaction - used in the 221 * async callback to match with the original request. 222 * @param peerId ID of the peer. Obtained through previous communication (a 223 * match indication). 224 * @param message Message byte array to send to the peer. 225 * @param messageId A message ID provided by caller to be used in any 226 * callbacks related to the message (success/failure). 227 */ sendMessage(short transactionId, int peerId, byte[] message, int messageId)228 public boolean sendMessage(short transactionId, int peerId, byte[] message, int messageId) { 229 PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.get(peerId); 230 if (peerInfo == null) { 231 Log.e(TAG, "sendMessage: attempting to send a message to an address which didn't " 232 + "match/contact us"); 233 try { 234 mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE); 235 } catch (RemoteException e) { 236 Log.e(TAG, "sendMessage: RemoteException=" + e); 237 } 238 return false; 239 } 240 241 boolean success = mWifiAwareNativeApi.sendMessage(transactionId, mPubSubId, 242 peerInfo.mInstanceId, peerInfo.mMac, message, messageId); 243 if (!success) { 244 try { 245 mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE); 246 } catch (RemoteException e) { 247 Log.e(TAG, "sendMessage: RemoteException=" + e); 248 } 249 return false; 250 } 251 252 return success; 253 } 254 255 /** 256 * Callback from HAL when a discovery occurs - i.e. when a match to an 257 * active subscription request or to a solicited publish request occurs. 258 * Propagates to client if registered. 259 * 260 * @param requestorInstanceId The ID used to identify the peer in this 261 * matched session. 262 * @param peerMac The MAC address of the peer. Never propagated to client 263 * due to privacy concerns. 264 * @param serviceSpecificInfo Information from the discovery advertisement 265 * (usually not used in the match decisions). 266 * @param matchFilter The filter from the discovery advertisement (which was 267 * used in the match decision). 268 * @param rangingIndication Bit mask indicating the type of ranging event triggered. 269 * @param rangeMm The range to the peer in mm (valid if rangingIndication specifies ingress 270 * or egress events - i.e. non-zero). 271 */ onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndication, int rangeMm)272 public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo, 273 byte[] matchFilter, int rangingIndication, int rangeMm) { 274 int peerId = getPeerIdOrAddIfNew(requestorInstanceId, peerMac); 275 276 try { 277 if (rangingIndication == 0) { 278 mCallback.onMatch(peerId, serviceSpecificInfo, matchFilter); 279 } else { 280 mCallback.onMatchWithDistance(peerId, serviceSpecificInfo, matchFilter, rangeMm); 281 } 282 } catch (RemoteException e) { 283 Log.w(TAG, "onMatch: RemoteException (FYI): " + e); 284 } 285 } 286 287 /** 288 * Callback from HAL when a discovered peer is lost - i.e. when a discovered peer with a matched 289 * session is no longer visible. 290 * 291 * @param requestorInstanceId The ID used to identify the peer in this matched session. 292 */ onMatchExpired(int requestorInstanceId)293 public void onMatchExpired(int requestorInstanceId) { 294 int peerId = 0; 295 for (int i = 0; i < mPeerInfoByRequestorInstanceId.size(); ++i) { 296 PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.valueAt(i); 297 if (peerInfo.mInstanceId == requestorInstanceId) { 298 peerId = mPeerInfoByRequestorInstanceId.keyAt(i); 299 mPeerInfoByRequestorInstanceId.delete(peerId); 300 break; 301 } 302 } 303 if (peerId == 0) { 304 return; 305 } 306 307 try { 308 mCallback.onMatchExpired(peerId); 309 } catch (RemoteException e) { 310 Log.w(TAG, "onMatch: RemoteException (FYI): " + e); 311 } 312 } 313 314 /** 315 * Callback from HAL when a message is received from a peer in a discovery 316 * session. Propagated to client if registered. 317 * 318 * @param requestorInstanceId An ID used to identify the peer. 319 * @param peerMac The MAC address of the peer sending the message. This 320 * information is never propagated to the client due to privacy 321 * concerns. 322 * @param message The received message. 323 */ onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message)324 public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message) { 325 int peerId = getPeerIdOrAddIfNew(requestorInstanceId, peerMac); 326 327 try { 328 mCallback.onMessageReceived(peerId, message); 329 } catch (RemoteException e) { 330 Log.w(TAG, "onMessageReceived: RemoteException (FYI): " + e); 331 } 332 } 333 getPeerIdOrAddIfNew(int requestorInstanceId, byte[] peerMac)334 private int getPeerIdOrAddIfNew(int requestorInstanceId, byte[] peerMac) { 335 for (int i = 0; i < mPeerInfoByRequestorInstanceId.size(); ++i) { 336 PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.valueAt(i); 337 if (peerInfo.mInstanceId == requestorInstanceId && Arrays.equals(peerMac, 338 peerInfo.mMac)) { 339 return mPeerInfoByRequestorInstanceId.keyAt(i); 340 } 341 } 342 343 int newPeerId = sNextPeerIdToBeAllocated++; 344 PeerInfo newPeerInfo = new PeerInfo(requestorInstanceId, peerMac); 345 mPeerInfoByRequestorInstanceId.put(newPeerId, newPeerInfo); 346 347 if (mDbg) { 348 Log.v(TAG, "New peer info: peerId=" + newPeerId + ", peerInfo=" + newPeerInfo); 349 } 350 351 return newPeerId; 352 } 353 354 /** 355 * Dump the internal state of the class. 356 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)357 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 358 pw.println("AwareSessionState:"); 359 pw.println(" mSessionId: " + mSessionId); 360 pw.println(" mIsPublishSession: " + mIsPublishSession); 361 pw.println(" mPubSubId: " + mPubSubId); 362 pw.println(" mPeerInfoByRequestorInstanceId: [" + mPeerInfoByRequestorInstanceId + "]"); 363 } 364 } 365