1 /* 2 * Copyright (C) 2017 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.IWifiNanIfaceEventCallback; 20 import android.hardware.wifi.V1_0.NanCapabilities; 21 import android.hardware.wifi.V1_0.NanClusterEventInd; 22 import android.hardware.wifi.V1_0.NanClusterEventType; 23 import android.hardware.wifi.V1_0.NanDataPathConfirmInd; 24 import android.hardware.wifi.V1_0.NanDataPathRequestInd; 25 import android.hardware.wifi.V1_0.NanFollowupReceivedInd; 26 import android.hardware.wifi.V1_0.NanMatchInd; 27 import android.hardware.wifi.V1_0.NanStatusType; 28 import android.hardware.wifi.V1_0.WifiNanStatus; 29 import android.os.ShellCommand; 30 import android.util.Log; 31 import android.util.SparseIntArray; 32 33 import libcore.util.HexEncoding; 34 35 import org.json.JSONException; 36 import org.json.JSONObject; 37 38 import java.io.FileDescriptor; 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 43 /** 44 * Manages the callbacks from Wi-Fi Aware HIDL (HAL). 45 */ 46 public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub implements 47 WifiAwareShellCommand.DelegatedShellCommand { 48 private static final String TAG = "WifiAwareNativeCallback"; 49 private static final boolean DBG = false; 50 private static final boolean VDBG = false; 51 52 private final WifiAwareStateManager mWifiAwareStateManager; 53 54 public WifiAwareNativeCallback(WifiAwareStateManager wifiAwareStateManager) { 55 mWifiAwareStateManager = wifiAwareStateManager; 56 } 57 58 /* 59 * Counts of callbacks from HAL. Retrievable through shell command. 60 */ 61 private static final int CB_EV_CLUSTER = 0; 62 private static final int CB_EV_DISABLED = 1; 63 private static final int CB_EV_PUBLISH_TERMINATED = 2; 64 private static final int CB_EV_SUBSCRIBE_TERMINATED = 3; 65 private static final int CB_EV_MATCH = 4; 66 private static final int CB_EV_MATCH_EXPIRED = 5; 67 private static final int CB_EV_FOLLOWUP_RECEIVED = 6; 68 private static final int CB_EV_TRANSMIT_FOLLOWUP = 7; 69 private static final int CB_EV_DATA_PATH_REQUEST = 8; 70 private static final int CB_EV_DATA_PATH_CONFIRM = 9; 71 private static final int CB_EV_DATA_PATH_TERMINATED = 10; 72 73 private SparseIntArray mCallbackCounter = new SparseIntArray(); 74 75 private void incrementCbCount(int callbackId) { 76 mCallbackCounter.put(callbackId, mCallbackCounter.get(callbackId) + 1); 77 } 78 79 /** 80 * Interpreter of adb shell command 'adb shell wifiaware native_cb ...'. 81 * 82 * @return -1 if parameter not recognized or invalid value, 0 otherwise. 83 */ 84 @Override 85 public int onCommand(ShellCommand parentShell) { 86 final PrintWriter pwe = parentShell.getErrPrintWriter(); 87 final PrintWriter pwo = parentShell.getOutPrintWriter(); 88 89 String subCmd = parentShell.getNextArgRequired(); 90 if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'"); 91 switch (subCmd) { 92 case "get_cb_count": { 93 String option = parentShell.getNextOption(); 94 Log.v(TAG, "option='" + option + "'"); 95 boolean reset = false; 96 if (option != null) { 97 if ("--reset".equals(option)) { 98 reset = true; 99 } else { 100 pwe.println("Unknown option to 'get_cb_count'"); 101 return -1; 102 } 103 } 104 105 JSONObject j = new JSONObject(); 106 try { 107 for (int i = 0; i < mCallbackCounter.size(); ++i) { 108 j.put(Integer.toString(mCallbackCounter.keyAt(i)), 109 mCallbackCounter.valueAt(i)); 110 } 111 } catch (JSONException e) { 112 Log.e(TAG, "onCommand: get_cb_count e=" + e); 113 } 114 pwo.println(j.toString()); 115 if (reset) { 116 mCallbackCounter.clear(); 117 } 118 return 0; 119 } 120 default: 121 pwe.println("Unknown 'wifiaware native_cb <cmd>'"); 122 } 123 124 return -1; 125 } 126 127 @Override 128 public void onReset() { 129 // NOP (onReset is intended for configuration reset - not data reset) 130 } 131 132 @Override 133 public void onHelp(String command, ShellCommand parentShell) { 134 final PrintWriter pw = parentShell.getOutPrintWriter(); 135 136 pw.println(" " + command); 137 pw.println(" get_cb_count [--reset]: gets the number of callbacks (and optionally reset " 138 + "count)"); 139 } 140 141 @Override 142 public void notifyCapabilitiesResponse(short id, WifiNanStatus status, 143 NanCapabilities capabilities) { 144 if (VDBG) { 145 Log.v(TAG, "notifyCapabilitiesResponse: id=" + id + ", status=" + statusString(status) 146 + ", capabilities=" + capabilities); 147 } 148 149 if (status.status == NanStatusType.SUCCESS) { 150 Capabilities frameworkCapabilities = new Capabilities(); 151 frameworkCapabilities.maxConcurrentAwareClusters = capabilities.maxConcurrentClusters; 152 frameworkCapabilities.maxPublishes = capabilities.maxPublishes; 153 frameworkCapabilities.maxSubscribes = capabilities.maxSubscribes; 154 frameworkCapabilities.maxServiceNameLen = capabilities.maxServiceNameLen; 155 frameworkCapabilities.maxMatchFilterLen = capabilities.maxMatchFilterLen; 156 frameworkCapabilities.maxTotalMatchFilterLen = capabilities.maxTotalMatchFilterLen; 157 frameworkCapabilities.maxServiceSpecificInfoLen = 158 capabilities.maxServiceSpecificInfoLen; 159 frameworkCapabilities.maxExtendedServiceSpecificInfoLen = 160 capabilities.maxExtendedServiceSpecificInfoLen; 161 frameworkCapabilities.maxNdiInterfaces = capabilities.maxNdiInterfaces; 162 frameworkCapabilities.maxNdpSessions = capabilities.maxNdpSessions; 163 frameworkCapabilities.maxAppInfoLen = capabilities.maxAppInfoLen; 164 frameworkCapabilities.maxQueuedTransmitMessages = 165 capabilities.maxQueuedTransmitFollowupMsgs; 166 frameworkCapabilities.maxSubscribeInterfaceAddresses = 167 capabilities.maxSubscribeInterfaceAddresses; 168 frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites; 169 170 mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities); 171 } else { 172 Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " (" 173 + status.description + ")"); 174 } 175 } 176 177 @Override 178 public void notifyEnableResponse(short id, WifiNanStatus status) { 179 if (VDBG) Log.v(TAG, "notifyEnableResponse: id=" + id + ", status=" + statusString(status)); 180 181 if (status.status == NanStatusType.ALREADY_ENABLED) { 182 Log.wtf(TAG, "notifyEnableResponse: id=" + id + ", already enabled!?"); 183 } 184 185 if (status.status == NanStatusType.SUCCESS 186 || status.status == NanStatusType.ALREADY_ENABLED) { 187 mWifiAwareStateManager.onConfigSuccessResponse(id); 188 } else { 189 mWifiAwareStateManager.onConfigFailedResponse(id, status.status); 190 } 191 } 192 193 @Override 194 public void notifyConfigResponse(short id, WifiNanStatus status) { 195 if (VDBG) Log.v(TAG, "notifyConfigResponse: id=" + id + ", status=" + statusString(status)); 196 197 if (status.status == NanStatusType.SUCCESS) { 198 mWifiAwareStateManager.onConfigSuccessResponse(id); 199 } else { 200 mWifiAwareStateManager.onConfigFailedResponse(id, status.status); 201 } 202 } 203 204 @Override 205 public void notifyDisableResponse(short id, WifiNanStatus status) { 206 if (VDBG) { 207 Log.v(TAG, "notifyDisableResponse: id=" + id + ", status=" + statusString(status)); 208 } 209 210 if (status.status != NanStatusType.SUCCESS) { 211 Log.e(TAG, "notifyDisableResponse: failure - code=" + status.status + " (" 212 + status.description + ")"); 213 } 214 mWifiAwareStateManager.onDisableResponse(id, status.status); 215 } 216 217 @Override 218 public void notifyStartPublishResponse(short id, WifiNanStatus status, byte publishId) { 219 if (VDBG) { 220 Log.v(TAG, "notifyStartPublishResponse: id=" + id + ", status=" + statusString(status) 221 + ", publishId=" + publishId); 222 } 223 224 if (status.status == NanStatusType.SUCCESS) { 225 mWifiAwareStateManager.onSessionConfigSuccessResponse(id, true, publishId); 226 } else { 227 mWifiAwareStateManager.onSessionConfigFailResponse(id, true, status.status); 228 } 229 } 230 231 @Override 232 public void notifyStopPublishResponse(short id, WifiNanStatus status) { 233 if (VDBG) { 234 Log.v(TAG, "notifyStopPublishResponse: id=" + id + ", status=" + statusString(status)); 235 } 236 237 if (status.status == NanStatusType.SUCCESS) { 238 // NOP 239 } else { 240 Log.e(TAG, "notifyStopPublishResponse: failure - code=" + status.status + " (" 241 + status.description + ")"); 242 } 243 } 244 245 @Override 246 public void notifyStartSubscribeResponse(short id, WifiNanStatus status, byte subscribeId) { 247 if (VDBG) { 248 Log.v(TAG, "notifyStartSubscribeResponse: id=" + id + ", status=" + statusString(status) 249 + ", subscribeId=" + subscribeId); 250 } 251 252 if (status.status == NanStatusType.SUCCESS) { 253 mWifiAwareStateManager.onSessionConfigSuccessResponse(id, false, subscribeId); 254 } else { 255 mWifiAwareStateManager.onSessionConfigFailResponse(id, false, status.status); 256 } 257 } 258 259 @Override 260 public void notifyStopSubscribeResponse(short id, WifiNanStatus status) { 261 if (VDBG) { 262 Log.v(TAG, "notifyStopSubscribeResponse: id=" + id + ", status=" 263 + statusString(status)); 264 } 265 266 if (status.status == NanStatusType.SUCCESS) { 267 // NOP 268 } else { 269 Log.e(TAG, "notifyStopSubscribeResponse: failure - code=" + status.status + " (" 270 + status.description + ")"); 271 } 272 } 273 274 @Override 275 public void notifyTransmitFollowupResponse(short id, WifiNanStatus status) { 276 if (VDBG) { 277 Log.v(TAG, "notifyTransmitFollowupResponse: id=" + id + ", status=" 278 + statusString(status)); 279 } 280 281 if (status.status == NanStatusType.SUCCESS) { 282 mWifiAwareStateManager.onMessageSendQueuedSuccessResponse(id); 283 } else { 284 mWifiAwareStateManager.onMessageSendQueuedFailResponse(id, status.status); 285 } 286 } 287 288 @Override 289 public void notifyCreateDataInterfaceResponse(short id, WifiNanStatus status) { 290 if (VDBG) { 291 Log.v(TAG, "notifyCreateDataInterfaceResponse: id=" + id + ", status=" 292 + statusString(status)); 293 } 294 295 mWifiAwareStateManager.onCreateDataPathInterfaceResponse(id, 296 status.status == NanStatusType.SUCCESS, status.status); 297 } 298 299 @Override 300 public void notifyDeleteDataInterfaceResponse(short id, WifiNanStatus status) { 301 if (VDBG) { 302 Log.v(TAG, "notifyDeleteDataInterfaceResponse: id=" + id + ", status=" 303 + statusString(status)); 304 } 305 306 mWifiAwareStateManager.onDeleteDataPathInterfaceResponse(id, 307 status.status == NanStatusType.SUCCESS, status.status); 308 } 309 310 @Override 311 public void notifyInitiateDataPathResponse(short id, WifiNanStatus status, 312 int ndpInstanceId) { 313 if (VDBG) { 314 Log.v(TAG, "notifyInitiateDataPathResponse: id=" + id + ", status=" 315 + statusString(status) + ", ndpInstanceId=" + ndpInstanceId); 316 } 317 318 if (status.status == NanStatusType.SUCCESS) { 319 mWifiAwareStateManager.onInitiateDataPathResponseSuccess(id, ndpInstanceId); 320 } else { 321 mWifiAwareStateManager.onInitiateDataPathResponseFail(id, status.status); 322 } 323 } 324 325 @Override 326 public void notifyRespondToDataPathIndicationResponse(short id, WifiNanStatus status) { 327 if (VDBG) { 328 Log.v(TAG, "notifyRespondToDataPathIndicationResponse: id=" + id 329 + ", status=" + statusString(status)); 330 } 331 332 mWifiAwareStateManager.onRespondToDataPathSetupRequestResponse(id, 333 status.status == NanStatusType.SUCCESS, status.status); 334 } 335 336 @Override 337 public void notifyTerminateDataPathResponse(short id, WifiNanStatus status) { 338 if (VDBG) { 339 Log.v(TAG, "notifyTerminateDataPathResponse: id=" + id + ", status=" 340 + statusString(status)); 341 } 342 343 mWifiAwareStateManager.onEndDataPathResponse(id, status.status == NanStatusType.SUCCESS, 344 status.status); 345 } 346 347 @Override 348 public void eventClusterEvent(NanClusterEventInd event) { 349 if (VDBG) { 350 Log.v(TAG, "eventClusterEvent: eventType=" + event.eventType + ", addr=" 351 + String.valueOf(HexEncoding.encode(event.addr))); 352 } 353 incrementCbCount(CB_EV_CLUSTER); 354 355 if (event.eventType == NanClusterEventType.DISCOVERY_MAC_ADDRESS_CHANGED) { 356 mWifiAwareStateManager.onInterfaceAddressChangeNotification(event.addr); 357 } else if (event.eventType == NanClusterEventType.STARTED_CLUSTER) { 358 mWifiAwareStateManager.onClusterChangeNotification( 359 WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED, event.addr); 360 } else if (event.eventType == NanClusterEventType.JOINED_CLUSTER) { 361 mWifiAwareStateManager.onClusterChangeNotification( 362 WifiAwareClientState.CLUSTER_CHANGE_EVENT_JOINED, event.addr); 363 } else { 364 Log.e(TAG, "eventClusterEvent: invalid eventType=" + event.eventType); 365 } 366 } 367 368 @Override 369 public void eventDisabled(WifiNanStatus status) { 370 if (VDBG) Log.v(TAG, "eventDisabled: status=" + statusString(status)); 371 incrementCbCount(CB_EV_DISABLED); 372 373 mWifiAwareStateManager.onAwareDownNotification(status.status); 374 } 375 376 @Override 377 public void eventPublishTerminated(byte sessionId, WifiNanStatus status) { 378 if (VDBG) { 379 Log.v(TAG, "eventPublishTerminated: sessionId=" + sessionId + ", status=" 380 + statusString(status)); 381 } 382 incrementCbCount(CB_EV_PUBLISH_TERMINATED); 383 384 mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, true); 385 } 386 387 @Override 388 public void eventSubscribeTerminated(byte sessionId, WifiNanStatus status) { 389 if (VDBG) { 390 Log.v(TAG, "eventSubscribeTerminated: sessionId=" + sessionId + ", status=" 391 + statusString(status)); 392 } 393 incrementCbCount(CB_EV_SUBSCRIBE_TERMINATED); 394 395 mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, false); 396 } 397 398 @Override 399 public void eventMatch(NanMatchInd event) { 400 if (VDBG) { 401 Log.v(TAG, "eventMatch: discoverySessionId=" + event.discoverySessionId + ", peerId=" 402 + event.peerId + ", addr=" + String.valueOf(HexEncoding.encode(event.addr)) 403 + ", serviceSpecificInfo=" + Arrays.toString( 404 convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()=" 405 + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size()) 406 + ", matchFilter=" + Arrays.toString( 407 convertArrayListToNativeByteArray(event.matchFilter)) + ", mf.size()=" + ( 408 event.matchFilter == null ? 0 : event.matchFilter.size())); 409 } 410 incrementCbCount(CB_EV_MATCH); 411 412 mWifiAwareStateManager.onMatchNotification(event.discoverySessionId, event.peerId, 413 event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo), 414 convertArrayListToNativeByteArray(event.matchFilter)); 415 } 416 417 @Override 418 public void eventMatchExpired(byte discoverySessionId, int peerId) { 419 if (VDBG) { 420 Log.v(TAG, "eventMatchExpired: discoverySessionId=" + discoverySessionId 421 + ", peerId=" + peerId); 422 } 423 incrementCbCount(CB_EV_MATCH_EXPIRED); 424 425 // NOP 426 } 427 428 @Override 429 public void eventFollowupReceived(NanFollowupReceivedInd event) { 430 if (VDBG) { 431 Log.v(TAG, "eventFollowupReceived: discoverySessionId=" + event.discoverySessionId 432 + ", peerId=" + event.peerId + ", addr=" + String.valueOf( 433 HexEncoding.encode(event.addr)) + ", serviceSpecificInfo=" + Arrays.toString( 434 convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()=" 435 + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size())); 436 } 437 incrementCbCount(CB_EV_FOLLOWUP_RECEIVED); 438 439 mWifiAwareStateManager.onMessageReceivedNotification(event.discoverySessionId, event.peerId, 440 event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo)); 441 } 442 443 @Override 444 public void eventTransmitFollowup(short id, WifiNanStatus status) { 445 if (VDBG) { 446 Log.v(TAG, "eventTransmitFollowup: id=" + id + ", status=" + statusString(status)); 447 } 448 incrementCbCount(CB_EV_TRANSMIT_FOLLOWUP); 449 450 if (status.status == NanStatusType.SUCCESS) { 451 mWifiAwareStateManager.onMessageSendSuccessNotification(id); 452 } else { 453 mWifiAwareStateManager.onMessageSendFailNotification(id, status.status); 454 } 455 } 456 457 @Override 458 public void eventDataPathRequest(NanDataPathRequestInd event) { 459 if (VDBG) { 460 Log.v(TAG, "eventDataPathRequest: discoverySessionId=" + event.discoverySessionId 461 + ", peerDiscMacAddr=" + String.valueOf( 462 HexEncoding.encode(event.peerDiscMacAddr)) + ", ndpInstanceId=" 463 + event.ndpInstanceId); 464 } 465 incrementCbCount(CB_EV_DATA_PATH_REQUEST); 466 467 mWifiAwareStateManager.onDataPathRequestNotification(event.discoverySessionId, 468 event.peerDiscMacAddr, event.ndpInstanceId); 469 } 470 471 @Override 472 public void eventDataPathConfirm(NanDataPathConfirmInd event) { 473 if (VDBG) { 474 Log.v(TAG, "onDataPathConfirm: ndpInstanceId=" + event.ndpInstanceId 475 + ", peerNdiMacAddr=" + String.valueOf(HexEncoding.encode(event.peerNdiMacAddr)) 476 + ", dataPathSetupSuccess=" + event.dataPathSetupSuccess + ", reason=" 477 + event.status.status); 478 } 479 incrementCbCount(CB_EV_DATA_PATH_CONFIRM); 480 481 mWifiAwareStateManager.onDataPathConfirmNotification(event.ndpInstanceId, 482 event.peerNdiMacAddr, event.dataPathSetupSuccess, event.status.status, 483 convertArrayListToNativeByteArray(event.appInfo)); 484 } 485 486 @Override 487 public void eventDataPathTerminated(int ndpInstanceId) { 488 if (VDBG) Log.v(TAG, "eventDataPathTerminated: ndpInstanceId=" + ndpInstanceId); 489 incrementCbCount(CB_EV_DATA_PATH_TERMINATED); 490 491 mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId); 492 } 493 494 /** 495 * Dump the internal state of the class. 496 */ 497 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 498 pw.println("WifiAwareNativeCallback:"); 499 pw.println(" mCallbackCounter: " + mCallbackCounter); 500 } 501 502 503 // utilities 504 505 /** 506 * Converts an ArrayList<Byte> to a byte[]. 507 * 508 * @param from The input ArrayList<Byte></Byte> to convert from. 509 * 510 * @return A newly allocated byte[]. 511 */ 512 private byte[] convertArrayListToNativeByteArray(ArrayList<Byte> from) { 513 if (from == null) { 514 return null; 515 } 516 517 byte[] to = new byte[from.size()]; 518 for (int i = 0; i < from.size(); ++i) { 519 to[i] = from.get(i); 520 } 521 return to; 522 } 523 524 private static String statusString(WifiNanStatus status) { 525 if (status == null) { 526 return "status=null"; 527 } 528 StringBuilder sb = new StringBuilder(); 529 sb.append(status.status).append(" (").append(status.description).append(")"); 530 return sb.toString(); 531 } 532 } 533