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.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