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