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