Home | History | Annotate | Download | only in wifiaware
      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