• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.snippet;
2 
3 import static java.nio.charset.StandardCharsets.UTF_8;
4 
5 import android.content.Context;
6 import android.net.wifi.aware.DiscoverySession;
7 import android.net.wifi.aware.PeerHandle;
8 import android.net.wifi.aware.PublishConfig;
9 import android.net.wifi.aware.SubscribeConfig;
10 import android.net.wifi.aware.WifiAwareManager;
11 import android.net.wifi.aware.WifiAwareSession;
12 import android.os.Handler;
13 import android.os.HandlerThread;
14 import android.util.Log;
15 import android.util.Pair;
16 import androidx.test.platform.app.InstrumentationRegistry;
17 import com.google.android.mobly.snippet.Snippet;
18 import com.google.android.mobly.snippet.rpc.Rpc;
19 import com.google.common.collect.ImmutableSet;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.List;
23 
24 /** An example snippet class with a simple Rpc. */
25 public class WifiAwareSnippet implements Snippet {
26 
27   private static class WifiAwareSnippetException extends Exception {
28     private static final long SERIAL_VERSION_UID = 1;
29 
WifiAwareSnippetException(String msg)30     public WifiAwareSnippetException(String msg) {
31       super(msg);
32     }
33 
WifiAwareSnippetException(String msg, Throwable err)34     public WifiAwareSnippetException(String msg, Throwable err) {
35       super(msg, err);
36     }
37   }
38 
39   private static final String TAG = "WifiAwareSnippet";
40 
41   private static final String SERVICE_NAME = "CtsVerifierTestService";
42   private static final byte[] MATCH_FILTER_BYTES = "bytes used for matching".getBytes(UTF_8);
43   private static final byte[] PUB_SSI = "Extra bytes in the publisher discovery".getBytes(UTF_8);
44   private static final byte[] SUB_SSI =
45       "Arbitrary bytes for the subscribe discovery".getBytes(UTF_8);
46   private static final int LARGE_ENOUGH_DISTANCE = 100000; // 100 meters
47 
48   private final WifiAwareManager wifiAwareManager;
49 
50   private final Context context;
51 
52   private final HandlerThread handlerThread;
53 
54   private final Handler handler;
55 
56   private WifiAwareSession wifiAwareSession;
57   private DiscoverySession wifiAwareDiscoverySession;
58   private CallbackUtils.DiscoveryCb discoveryCb;
59   private PeerHandle peerHandle;
60 
WifiAwareSnippet()61   public WifiAwareSnippet() {
62     context = InstrumentationRegistry.getInstrumentation().getTargetContext();
63     wifiAwareManager = context.getSystemService(WifiAwareManager.class);
64     handlerThread = new HandlerThread("Snippet-Aware");
65     handlerThread.start();
66     handler = new Handler(handlerThread.getLooper());
67   }
68 
69   @Rpc(description = "Execute attach.")
attach()70   public void attach() throws InterruptedException, WifiAwareSnippetException {
71     CallbackUtils.AttachCb attachCb = new CallbackUtils.AttachCb();
72     wifiAwareManager.attach(attachCb, handler);
73     Pair<CallbackUtils.AttachCb.CallbackCode, WifiAwareSession> results = attachCb.waitForAttach();
74     if (results.first != CallbackUtils.AttachCb.CallbackCode.ON_ATTACHED) {
75       throw new WifiAwareSnippetException(String.format("executeTest: attach %s", results.first));
76     }
77     wifiAwareSession = results.second;
78     if (wifiAwareSession == null) {
79       throw new WifiAwareSnippetException(
80           "executeTest: attach callback succeeded but null session returned!?");
81     }
82   }
83 
84   @Rpc(description = "Execute subscribe.")
subscribe(Boolean isUnsolicited, Boolean isRangingRequired)85   public void subscribe(Boolean isUnsolicited, Boolean isRangingRequired)
86       throws InterruptedException, WifiAwareSnippetException {
87     discoveryCb = new CallbackUtils.DiscoveryCb();
88 
89     List<byte[]> matchFilter = new ArrayList<>();
90     matchFilter.add(MATCH_FILTER_BYTES);
91     SubscribeConfig.Builder builder =
92         new SubscribeConfig.Builder()
93             .setServiceName(SERVICE_NAME)
94             .setServiceSpecificInfo(SUB_SSI)
95             .setMatchFilter(matchFilter)
96             .setSubscribeType(
97                 isUnsolicited
98                     ? SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE
99                     : SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE)
100             .setTerminateNotificationEnabled(true);
101 
102     if (isRangingRequired) {
103       // set up a distance that will always trigger - i.e. that we're already in that range
104       builder.setMaxDistanceMm(LARGE_ENOUGH_DISTANCE);
105     }
106     SubscribeConfig subscribeConfig = builder.build();
107     Log.d(TAG, "executeTestSubscriber: subscribeConfig=" + subscribeConfig);
108     wifiAwareSession.subscribe(subscribeConfig, discoveryCb, handler);
109 
110     // wait for results - subscribe session
111     CallbackUtils.DiscoveryCb.CallbackData callbackData =
112         discoveryCb.waitForCallbacks(
113             ImmutableSet.of(
114                 CallbackUtils.DiscoveryCb.CallbackCode.ON_SUBSCRIBE_STARTED,
115                 CallbackUtils.DiscoveryCb.CallbackCode.ON_SESSION_CONFIG_FAILED));
116     if (callbackData.callbackCode != CallbackUtils.DiscoveryCb.CallbackCode.ON_SUBSCRIBE_STARTED) {
117       throw new WifiAwareSnippetException(
118           String.format("executeTestSubscriber: subscribe %s", callbackData.callbackCode));
119     }
120     wifiAwareDiscoverySession = callbackData.subscribeDiscoverySession;
121     if (wifiAwareDiscoverySession == null) {
122       throw new WifiAwareSnippetException(
123           "executeTestSubscriber: subscribe succeeded but null session returned");
124     }
125     Log.d(TAG, "executeTestSubscriber: subscribe succeeded");
126 
127     // 3. wait for discovery
128     callbackData =
129         discoveryCb.waitForCallbacks(
130             ImmutableSet.of(
131                 isRangingRequired
132                     ? CallbackUtils.DiscoveryCb.CallbackCode.ON_SERVICE_DISCOVERED_WITH_RANGE
133                     : CallbackUtils.DiscoveryCb.CallbackCode.ON_SERVICE_DISCOVERED));
134 
135     if (callbackData.callbackCode == CallbackUtils.DiscoveryCb.CallbackCode.TIMEOUT) {
136       throw new WifiAwareSnippetException("executeTestSubscriber: waiting for discovery TIMEOUT");
137     }
138     peerHandle = callbackData.peerHandle;
139     if (!isRangingRequired) {
140       Log.d(TAG, "executeTestSubscriber: discovery");
141     } else {
142       Log.d(TAG, "executeTestSubscriber: discovery with range=" + callbackData.distanceMm);
143     }
144 
145     if (!Arrays.equals(PUB_SSI, callbackData.serviceSpecificInfo)) {
146       throw new WifiAwareSnippetException(
147           "executeTestSubscriber: discovery but SSI mismatch: rx='"
148               + new String(callbackData.serviceSpecificInfo, UTF_8)
149               + "'");
150     }
151     if (callbackData.matchFilter.size() != 1
152         || !Arrays.equals(MATCH_FILTER_BYTES, callbackData.matchFilter.get(0))) {
153       StringBuilder sb = new StringBuilder();
154       sb.append("size=").append(callbackData.matchFilter.size());
155       for (byte[] mf : callbackData.matchFilter) {
156         sb.append(", e='").append(new String(mf, UTF_8)).append("'");
157       }
158       throw new WifiAwareSnippetException(
159           "executeTestSubscriber: discovery but matchFilter mismatch: " + sb);
160     }
161     if (peerHandle == null) {
162       throw new WifiAwareSnippetException("executeTestSubscriber: discovery but null peerHandle");
163     }
164   }
165 
166   @Rpc(description = "Send message.")
sendMessage(int messageId, String message)167   public void sendMessage(int messageId, String message)
168       throws InterruptedException, WifiAwareSnippetException {
169     // 4. send message & wait for send status
170     wifiAwareDiscoverySession.sendMessage(peerHandle, messageId, message.getBytes(UTF_8));
171     CallbackUtils.DiscoveryCb.CallbackData callbackData =
172         discoveryCb.waitForCallbacks(
173             ImmutableSet.of(
174                 CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_SEND_SUCCEEDED,
175                 CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_SEND_FAILED));
176 
177     if (callbackData.callbackCode
178         != CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_SEND_SUCCEEDED) {
179       throw new WifiAwareSnippetException(
180           String.format("executeTestSubscriber: sendMessage %s", callbackData.callbackCode));
181     }
182     Log.d(TAG, "executeTestSubscriber: send message succeeded");
183     if (callbackData.messageId != messageId) {
184       throw new WifiAwareSnippetException(
185           "executeTestSubscriber: send message message ID mismatch: " + callbackData.messageId);
186     }
187   }
188 
189   @Rpc(description = "Create publish session.")
publish(Boolean isUnsolicited, Boolean isRangingRequired)190   public void publish(Boolean isUnsolicited, Boolean isRangingRequired)
191       throws WifiAwareSnippetException, InterruptedException {
192     discoveryCb = new CallbackUtils.DiscoveryCb();
193 
194     // 2. publish
195     List<byte[]> matchFilter = new ArrayList<>();
196     matchFilter.add(MATCH_FILTER_BYTES);
197     PublishConfig publishConfig =
198         new PublishConfig.Builder()
199             .setServiceName(SERVICE_NAME)
200             .setServiceSpecificInfo(PUB_SSI)
201             .setMatchFilter(matchFilter)
202             .setPublishType(
203                 isUnsolicited
204                     ? PublishConfig.PUBLISH_TYPE_UNSOLICITED
205                     : PublishConfig.PUBLISH_TYPE_SOLICITED)
206             .setTerminateNotificationEnabled(true)
207             .setRangingEnabled(isRangingRequired)
208             .build();
209     Log.d(TAG, "executeTestPublisher: publishConfig=" + publishConfig);
210     wifiAwareSession.publish(publishConfig, discoveryCb, handler);
211 
212     //    wait for results - publish session
213     CallbackUtils.DiscoveryCb.CallbackData callbackData =
214         discoveryCb.waitForCallbacks(
215             ImmutableSet.of(
216                 CallbackUtils.DiscoveryCb.CallbackCode.ON_PUBLISH_STARTED,
217                 CallbackUtils.DiscoveryCb.CallbackCode.ON_SESSION_CONFIG_FAILED));
218     if (callbackData.callbackCode != CallbackUtils.DiscoveryCb.CallbackCode.ON_PUBLISH_STARTED) {
219       throw new WifiAwareSnippetException(
220           String.format("executeTestPublisher: publish %s", callbackData.callbackCode));
221     }
222     wifiAwareDiscoverySession = callbackData.publishDiscoverySession;
223     if (wifiAwareDiscoverySession == null) {
224       throw new WifiAwareSnippetException(
225           "executeTestPublisher: publish succeeded but null session returned");
226     }
227     Log.d(TAG, "executeTestPublisher: publish succeeded");
228   }
229 
230   @Rpc(description = "Receive message.")
receiveMessage()231   public String receiveMessage() throws WifiAwareSnippetException, InterruptedException {
232     // 3. wait to receive message.
233     CallbackUtils.DiscoveryCb.CallbackData callbackData =
234         discoveryCb.waitForCallbacks(
235             ImmutableSet.of(CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_RECEIVED));
236     peerHandle = callbackData.peerHandle;
237     Log.d(TAG, "executeTestPublisher: received message");
238 
239     if (peerHandle == null) {
240       throw new WifiAwareSnippetException(
241           "executeTestPublisher: received message but peerHandle is null!?");
242     }
243     return new String(callbackData.serviceSpecificInfo, UTF_8);
244   }
245 
246   @Override
shutdown()247   public void shutdown() {
248     if (wifiAwareDiscoverySession != null) {
249       wifiAwareDiscoverySession.close();
250       wifiAwareDiscoverySession = null;
251     }
252     if (wifiAwareSession != null) {
253       wifiAwareSession.close();
254       wifiAwareSession = null;
255     }
256   }
257 }
258