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