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 17package com.android.server.wifi.aware; 18 19import android.hardware.wifi.V1_0.NanStatusType; 20import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; 21import android.net.wifi.aware.PublishConfig; 22import android.net.wifi.aware.SubscribeConfig; 23import android.os.RemoteException; 24import android.util.Log; 25import android.util.SparseArray; 26 27import libcore.util.HexEncoding; 28 29import java.io.FileDescriptor; 30import java.io.PrintWriter; 31import 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 */ 39public 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 { 55 PeerInfo(int instanceId, byte[] mac) { 56 mInstanceId = instanceId; 57 mMac = mac; 58 } 59 60 int mInstanceId; 61 byte[] mMac; 62 63 @Override 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 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 85 public int getSessionId() { 86 return mSessionId; 87 } 88 89 public int getPubSubId() { 90 return mPubSubId; 91 } 92 93 public boolean isPublishSession() { 94 return mIsPublishSession; 95 } 96 97 public boolean isRangingEnabled() { 98 return mIsRangingEnabled; 99 } 100 101 public long getCreationTime() { 102 return mCreationTime; 103 } 104 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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