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.cts.verifier.wifiaware; 18 19 import android.net.ConnectivityManager; 20 import android.net.Network; 21 import android.net.NetworkCapabilities; 22 import android.net.wifi.aware.AttachCallback; 23 import android.net.wifi.aware.DiscoverySessionCallback; 24 import android.net.wifi.aware.IdentityChangedListener; 25 import android.net.wifi.aware.PeerHandle; 26 import android.net.wifi.aware.PublishDiscoverySession; 27 import android.net.wifi.aware.SubscribeDiscoverySession; 28 import android.net.wifi.aware.WifiAwareSession; 29 import android.net.wifi.rtt.RangingResult; 30 import android.net.wifi.rtt.RangingResultCallback; 31 import android.util.Log; 32 import android.util.Pair; 33 34 import java.util.ArrayDeque; 35 import java.util.List; 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.TimeUnit; 38 39 /** 40 * Blocking callbacks for Wi-Fi Aware and Connectivity Manager. 41 */ 42 public class CallbackUtils { 43 private static final String TAG = "CallbackUtils"; 44 45 public static final int CALLBACK_TIMEOUT_SEC = 15; 46 47 /** 48 * Utility AttachCallback - provides mechanism to block execution with the 49 * waitForAttach method. 50 */ 51 public static class AttachCb extends AttachCallback { 52 public static final int TIMEOUT = -1; 53 public static final int ON_ATTACHED = 0; 54 public static final int ON_ATTACH_FAILED = 1; 55 56 private CountDownLatch mBlocker = new CountDownLatch(1); 57 private int mCallback = TIMEOUT; 58 private WifiAwareSession mWifiAwareSession = null; 59 60 @Override onAttached(WifiAwareSession session)61 public void onAttached(WifiAwareSession session) { 62 mCallback = ON_ATTACHED; 63 mWifiAwareSession = session; 64 mBlocker.countDown(); 65 } 66 67 @Override onAttachFailed()68 public void onAttachFailed() { 69 mCallback = ON_ATTACH_FAILED; 70 mBlocker.countDown(); 71 } 72 73 /** 74 * Wait (blocks) for any AttachCallback callback or timeout. 75 * 76 * @return A pair of values: the callback constant (or TIMEOUT) and the WifiAwareSession 77 * created when attach successful - null otherwise (attach failure or timeout). 78 */ waitForAttach()79 public Pair<Integer, WifiAwareSession> waitForAttach() throws InterruptedException { 80 if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) { 81 return new Pair<>(mCallback, mWifiAwareSession); 82 } 83 84 return new Pair<>(TIMEOUT, null); 85 } 86 } 87 88 /** 89 * Utility IdentityChangedListener - provides mechanism to block execution with the 90 * waitForIdentity method. Single shot listener - only listens for the first triggered 91 * callback. 92 */ 93 public static class IdentityListenerSingleShot extends IdentityChangedListener { 94 private CountDownLatch mBlocker = new CountDownLatch(1); 95 private byte[] mMac = null; 96 97 @Override onIdentityChanged(byte[] mac)98 public void onIdentityChanged(byte[] mac) { 99 if (mMac != null) { 100 return; 101 } 102 103 mMac = mac; 104 mBlocker.countDown(); 105 } 106 107 /** 108 * Wait (blocks) for the onIdentityChanged callback or a timeout. 109 * 110 * @return The MAC address returned by the onIdentityChanged() callback, or null on timeout. 111 */ waitForMac()112 public byte[] waitForMac() throws InterruptedException { 113 if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) { 114 return mMac; 115 } 116 117 return null; 118 } 119 } 120 121 /** 122 * Utility NetworkCallback - provides mechanism for blocking/serializing access with the 123 * waitForNetwork method. 124 */ 125 public static class NetworkCb extends ConnectivityManager.NetworkCallback { 126 private CountDownLatch mBlocker = new CountDownLatch(1); 127 private Network mNetwork = null; 128 private NetworkCapabilities mNetworkCapabilities = null; 129 130 @Override onUnavailable()131 public void onUnavailable() { 132 mNetworkCapabilities = null; 133 mBlocker.countDown(); 134 } 135 136 @Override onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities)137 public void onCapabilitiesChanged(Network network, 138 NetworkCapabilities networkCapabilities) { 139 mNetwork = network; 140 mNetworkCapabilities = networkCapabilities; 141 mBlocker.countDown(); 142 } 143 144 /** 145 * Wait (blocks) for Capabilities Changed callback - or timesout. 146 * 147 * @return Network + NetworkCapabilities (pair) if occurred, null otherwise. 148 */ waitForNetworkCapabilities()149 public Pair<Network, NetworkCapabilities> waitForNetworkCapabilities() 150 throws InterruptedException { 151 if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) { 152 return Pair.create(mNetwork, mNetworkCapabilities); 153 } 154 return null; 155 } 156 } 157 158 /** 159 * Utility DiscoverySessionCallback - provides mechanism to block/serialize Aware discovery 160 * operations using the waitForCallbacks() method. 161 */ 162 public static class DiscoveryCb extends DiscoverySessionCallback { 163 public static final int TIMEOUT = -1; 164 public static final int ON_PUBLISH_STARTED = 0x1 << 0; 165 public static final int ON_SUBSCRIBE_STARTED = 0x1 << 1; 166 public static final int ON_SESSION_CONFIG_UPDATED = 0x1 << 2; 167 public static final int ON_SESSION_CONFIG_FAILED = 0x1 << 3; 168 public static final int ON_SESSION_TERMINATED = 0x1 << 4; 169 public static final int ON_SERVICE_DISCOVERED = 0x1 << 5; 170 public static final int ON_MESSAGE_SEND_SUCCEEDED = 0x1 << 6; 171 public static final int ON_MESSAGE_SEND_FAILED = 0x1 << 7; 172 public static final int ON_MESSAGE_RECEIVED = 0x1 << 8; 173 public static final int ON_SERVICE_DISCOVERED_WITH_RANGE = 0x1 << 9; 174 175 /** 176 * Data container for all parameters which can be returned by any DiscoverySessionCallback 177 * callback. 178 */ 179 public static class CallbackData { CallbackData(int callback)180 public CallbackData(int callback) { 181 this.callback = callback; 182 } 183 184 public int callback; 185 186 public PublishDiscoverySession publishDiscoverySession; 187 public SubscribeDiscoverySession subscribeDiscoverySession; 188 public PeerHandle peerHandle; 189 public byte[] serviceSpecificInfo; 190 public List<byte[]> matchFilter; 191 public int messageId; 192 public int distanceMm; 193 } 194 195 private CountDownLatch mBlocker = null; 196 private int mWaitForCallbackMask = 0; 197 198 private final Object mLock = new Object(); 199 private ArrayDeque<CallbackData> mCallbackQueue = new ArrayDeque<>(); 200 processCallback(CallbackData callbackData)201 private void processCallback(CallbackData callbackData) { 202 synchronized (mLock) { 203 mCallbackQueue.addLast(callbackData); 204 if (mBlocker != null && (mWaitForCallbackMask & callbackData.callback) 205 == callbackData.callback) { 206 mBlocker.countDown(); 207 } 208 } 209 } 210 getAndRemoveFirst(int callbackMask)211 private CallbackData getAndRemoveFirst(int callbackMask) { 212 synchronized (mLock) { 213 for (CallbackData cbd : mCallbackQueue) { 214 if ((cbd.callback & callbackMask) == cbd.callback) { 215 mCallbackQueue.remove(cbd); 216 return cbd; 217 } 218 } 219 } 220 221 return null; 222 } 223 waitForCallbacks(int callbackMask, boolean timeout)224 private CallbackData waitForCallbacks(int callbackMask, boolean timeout) 225 throws InterruptedException { 226 synchronized (mLock) { 227 CallbackData cbd = getAndRemoveFirst(callbackMask); 228 if (cbd != null) { 229 return cbd; 230 } 231 232 mWaitForCallbackMask = callbackMask; 233 mBlocker = new CountDownLatch(1); 234 } 235 236 boolean finishedNormally = true; 237 if (timeout) { 238 finishedNormally = mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS); 239 } else { 240 mBlocker.await(); 241 } 242 if (finishedNormally) { 243 CallbackData cbd = getAndRemoveFirst(callbackMask); 244 if (cbd != null) { 245 return cbd; 246 } 247 248 Log.wtf(TAG, "DiscoveryCb.waitForCallback: callbackMask=" + callbackMask 249 + ": did not time-out but doesn't have any of the requested callbacks in " 250 + "the stack!?"); 251 // falling-through to TIMEOUT 252 } 253 254 return new CallbackData(TIMEOUT); 255 } 256 257 /** 258 * Wait for the specified callbacks - a bitmask of any of the ON_* constants. Returns the 259 * CallbackData structure whose CallbackData.callback specifies the callback which was 260 * triggered. The callback may be TIMEOUT. 261 * 262 * Note: other callbacks happening while while waiting for the specified callback(s) will 263 * be queued. 264 */ waitForCallbacks(int callbackMask)265 public CallbackData waitForCallbacks(int callbackMask) throws InterruptedException { 266 return waitForCallbacks(callbackMask, true); 267 } 268 269 /** 270 * Wait for the specified callbacks - a bitmask of any of the ON_* constants. Returns the 271 * CallbackData structure whose CallbackData.callback specifies the callback which was 272 * triggered. 273 * 274 * This call will not timeout - it can be interrupted though (which results in a thrown 275 * exception). 276 * 277 * Note: other callbacks happening while while waiting for the specified callback(s) will 278 * be queued. 279 */ waitForCallbacksNoTimeout(int callbackMask)280 public CallbackData waitForCallbacksNoTimeout(int callbackMask) 281 throws InterruptedException { 282 return waitForCallbacks(callbackMask, false); 283 } 284 285 @Override onPublishStarted(PublishDiscoverySession session)286 public void onPublishStarted(PublishDiscoverySession session) { 287 CallbackData callbackData = new CallbackData(ON_PUBLISH_STARTED); 288 callbackData.publishDiscoverySession = session; 289 processCallback(callbackData); 290 } 291 292 @Override onSubscribeStarted(SubscribeDiscoverySession session)293 public void onSubscribeStarted(SubscribeDiscoverySession session) { 294 CallbackData callbackData = new CallbackData(ON_SUBSCRIBE_STARTED); 295 callbackData.subscribeDiscoverySession = session; 296 processCallback(callbackData); 297 } 298 299 @Override onSessionConfigUpdated()300 public void onSessionConfigUpdated() { 301 CallbackData callbackData = new CallbackData(ON_SESSION_CONFIG_UPDATED); 302 processCallback(callbackData); 303 } 304 305 @Override onSessionConfigFailed()306 public void onSessionConfigFailed() { 307 CallbackData callbackData = new CallbackData(ON_SESSION_CONFIG_FAILED); 308 processCallback(callbackData); 309 } 310 311 @Override onSessionTerminated()312 public void onSessionTerminated() { 313 CallbackData callbackData = new CallbackData(ON_SESSION_TERMINATED); 314 processCallback(callbackData); 315 } 316 317 @Override onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter)318 public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, 319 List<byte[]> matchFilter) { 320 CallbackData callbackData = new CallbackData(ON_SERVICE_DISCOVERED); 321 callbackData.peerHandle = peerHandle; 322 callbackData.serviceSpecificInfo = serviceSpecificInfo; 323 callbackData.matchFilter = matchFilter; 324 processCallback(callbackData); 325 } 326 327 @Override onServiceDiscoveredWithinRange(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter, int distanceMm)328 public void onServiceDiscoveredWithinRange(PeerHandle peerHandle, 329 byte[] serviceSpecificInfo, 330 List<byte[]> matchFilter, int distanceMm) { 331 CallbackData callbackData = new CallbackData(ON_SERVICE_DISCOVERED_WITH_RANGE); 332 callbackData.peerHandle = peerHandle; 333 callbackData.serviceSpecificInfo = serviceSpecificInfo; 334 callbackData.matchFilter = matchFilter; 335 callbackData.distanceMm = distanceMm; 336 processCallback(callbackData); 337 } 338 339 @Override onMessageSendSucceeded(int messageId)340 public void onMessageSendSucceeded(int messageId) { 341 CallbackData callbackData = new CallbackData(ON_MESSAGE_SEND_SUCCEEDED); 342 callbackData.messageId = messageId; 343 processCallback(callbackData); 344 } 345 346 @Override onMessageSendFailed(int messageId)347 public void onMessageSendFailed(int messageId) { 348 CallbackData callbackData = new CallbackData(ON_MESSAGE_SEND_FAILED); 349 callbackData.messageId = messageId; 350 processCallback(callbackData); 351 } 352 353 @Override onMessageReceived(PeerHandle peerHandle, byte[] message)354 public void onMessageReceived(PeerHandle peerHandle, byte[] message) { 355 CallbackData callbackData = new CallbackData(ON_MESSAGE_RECEIVED); 356 callbackData.peerHandle = peerHandle; 357 callbackData.serviceSpecificInfo = message; 358 processCallback(callbackData); 359 } 360 } 361 362 /** 363 * Utility RangingResultCallback - provides mechanism for blocking/serializing access with the 364 * waitForRangingResults method. 365 */ 366 public static class RangingCb extends RangingResultCallback { 367 public static final int TIMEOUT = -1; 368 public static final int ON_FAILURE = 0; 369 public static final int ON_RESULTS = 1; 370 371 private CountDownLatch mBlocker = new CountDownLatch(1); 372 private int mStatus = TIMEOUT; 373 private List<RangingResult> mResults = null; 374 375 /** 376 * Wait (blocks) for Ranging results callbacks - or times-out. 377 * 378 * @return Pair of status & Ranging results if succeeded, null otherwise. 379 */ waitForRangingResults()380 public Pair<Integer, List<RangingResult>> waitForRangingResults() 381 throws InterruptedException { 382 if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) { 383 return new Pair<>(mStatus, mResults); 384 } 385 return new Pair<>(TIMEOUT, null); 386 } 387 388 @Override onRangingFailure(int code)389 public void onRangingFailure(int code) { 390 mStatus = ON_FAILURE; 391 mBlocker.countDown(); 392 } 393 394 @Override onRangingResults(List<RangingResult> results)395 public void onRangingResults(List<RangingResult> results) { 396 mStatus = ON_RESULTS; 397 mResults = results; 398 mBlocker.countDown(); 399 } 400 } 401 } 402