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.server.wifi.aware;
18 
19 import android.annotation.Nullable;
20 import android.net.wifi.OuiKeyedData;
21 import android.net.wifi.aware.AwarePairingConfig;
22 import android.net.wifi.aware.IdentityChangedListener;
23 import android.net.wifi.aware.WifiAwareChannelInfo;
24 import android.util.Log;
25 import android.util.SparseArray;
26 import android.util.SparseIntArray;
27 
28 import com.android.modules.utils.BasicShellCommandHandler;
29 import com.android.server.wifi.hal.WifiNanIface;
30 import com.android.server.wifi.hal.WifiNanIface.NanClusterEventType;
31 import com.android.server.wifi.hal.WifiNanIface.NanStatusCode;
32 
33 import org.json.JSONArray;
34 import org.json.JSONException;
35 import org.json.JSONObject;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 /**
43  * Manages the callbacks from Wi-Fi Aware HAL.
44  */
45 public class WifiAwareNativeCallback implements WifiNanIface.Callback,
46         WifiAwareShellCommand.DelegatedShellCommand {
47     private static final String TAG = "WifiAwareNativeCallback";
48     private boolean mVerboseLoggingEnabled = false;
49 
50     private final WifiAwareStateManager mWifiAwareStateManager;
51 
WifiAwareNativeCallback(WifiAwareStateManager wifiAwareStateManager)52     public WifiAwareNativeCallback(WifiAwareStateManager wifiAwareStateManager) {
53         mWifiAwareStateManager = wifiAwareStateManager;
54     }
55 
56     /**
57      * Enable/Disable verbose logging.
58      *
59      */
enableVerboseLogging(boolean verboseEnabled)60     public void enableVerboseLogging(boolean verboseEnabled) {
61         mVerboseLoggingEnabled = verboseEnabled;
62     }
63 
64     /*
65      * Counts of callbacks from HAL. Retrievable through shell command.
66      */
67     private static final int CB_EV_CLUSTER = 0;
68     private static final int CB_EV_DISABLED = 1;
69     private static final int CB_EV_PUBLISH_TERMINATED = 2;
70     private static final int CB_EV_SUBSCRIBE_TERMINATED = 3;
71     private static final int CB_EV_MATCH = 4;
72     private static final int CB_EV_MATCH_EXPIRED = 5;
73     private static final int CB_EV_FOLLOWUP_RECEIVED = 6;
74     private static final int CB_EV_TRANSMIT_FOLLOWUP = 7;
75     private static final int CB_EV_DATA_PATH_REQUEST = 8;
76     private static final int CB_EV_DATA_PATH_CONFIRM = 9;
77     private static final int CB_EV_DATA_PATH_TERMINATED = 10;
78     private static final int CB_EV_DATA_PATH_SCHED_UPDATE = 11;
79 
80     private final SparseIntArray mCallbackCounter = new SparseIntArray();
81     private final SparseArray<List<WifiAwareChannelInfo>> mChannelInfoPerNdp = new SparseArray<>();
82 
incrementCbCount(int callbackId)83     private void incrementCbCount(int callbackId) {
84         mCallbackCounter.put(callbackId, mCallbackCounter.get(callbackId) + 1);
85     }
86 
87     /**
88      * Interpreter of adb shell command 'adb shell cmd wifiaware native_cb ...'.
89      *
90      * @return -1 if parameter not recognized or invalid value, 0 otherwise.
91      */
92     @Override
onCommand(BasicShellCommandHandler parentShell)93     public int onCommand(BasicShellCommandHandler parentShell) {
94         final PrintWriter pwe = parentShell.getErrPrintWriter();
95         final PrintWriter pwo = parentShell.getOutPrintWriter();
96 
97         String subCmd = parentShell.getNextArgRequired();
98         switch (subCmd) {
99             case "get_cb_count": {
100                 String option = parentShell.getNextOption();
101                 boolean reset = false;
102                 if (option != null) {
103                     if ("--reset".equals(option)) {
104                         reset = true;
105                     } else {
106                         pwe.println("Unknown option to 'get_cb_count'");
107                         return -1;
108                     }
109                 }
110 
111                 JSONObject j = new JSONObject();
112                 try {
113                     for (int i = 0; i < mCallbackCounter.size(); ++i) {
114                         j.put(Integer.toString(mCallbackCounter.keyAt(i)),
115                                 mCallbackCounter.valueAt(i));
116                     }
117                 } catch (JSONException e) {
118                     Log.e(TAG, "onCommand: get_cb_count e=" + e);
119                 }
120                 pwo.println(j.toString());
121                 if (reset) {
122                     mCallbackCounter.clear();
123                 }
124                 return 0;
125             }
126             case  "get_channel_info": {
127                 String option = parentShell.getNextOption();
128                 if (option != null) {
129                     pwe.println("Unknown option to 'get_channel_info'");
130                     return -1;
131                 }
132                 String channelInfoString = convertChannelInfoToJsonString();
133                 pwo.println(channelInfoString);
134                 return 0;
135             }
136             default:
137                 pwe.println("Unknown 'wifiaware native_cb <cmd>'");
138         }
139 
140         return -1;
141     }
142 
143     @Override
onReset()144     public void onReset() {
145         // NOP (onReset is intended for configuration reset - not data reset)
146     }
147 
148     @Override
onHelp(String command, BasicShellCommandHandler parentShell)149     public void onHelp(String command, BasicShellCommandHandler parentShell) {
150         final PrintWriter pw = parentShell.getOutPrintWriter();
151 
152         pw.println("  " + command);
153         pw.println("    get_cb_count [--reset]: gets the number of callbacks (and optionally reset "
154                 + "count)");
155         pw.println("    get_channel_info: prints out existing NDP channel info as a JSON String");
156     }
157 
158     @Override
notifyCapabilitiesResponse(short id, Capabilities capabilities)159     public void notifyCapabilitiesResponse(short id, Capabilities capabilities) {
160         mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, capabilities);
161     }
162 
163     @Override
notifyEnableResponse(short id, int status)164     public void notifyEnableResponse(short id, int status) {
165         if (status == NanStatusCode.SUCCESS
166                 || status == NanStatusCode.ALREADY_ENABLED) {
167             mWifiAwareStateManager.onConfigSuccessResponse(id);
168         } else {
169             mWifiAwareStateManager.onConfigFailedResponse(id, status);
170         }
171     }
172 
173     @Override
notifyConfigResponse(short id, int status)174     public void notifyConfigResponse(short id, int status) {
175         if (status == NanStatusCode.SUCCESS) {
176             mWifiAwareStateManager.onConfigSuccessResponse(id);
177         } else {
178             mWifiAwareStateManager.onConfigFailedResponse(id, status);
179         }
180     }
181 
182     @Override
notifyDisableResponse(short id, int status)183     public void notifyDisableResponse(short id, int status) {
184         mWifiAwareStateManager.onDisableResponse(id, status);
185     }
186 
187     @Override
notifyStartPublishResponse(short id, int status, byte publishId)188     public void notifyStartPublishResponse(short id, int status, byte publishId) {
189         if (status == NanStatusCode.SUCCESS) {
190             mWifiAwareStateManager.onSessionConfigSuccessResponse(id, true, publishId);
191         } else {
192             mWifiAwareStateManager.onSessionConfigFailResponse(id, true, status);
193         }
194     }
195 
196     @Override
notifyStartSubscribeResponse(short id, int status, byte subscribeId)197     public void notifyStartSubscribeResponse(short id, int status, byte subscribeId) {
198         if (status == NanStatusCode.SUCCESS) {
199             mWifiAwareStateManager.onSessionConfigSuccessResponse(id, false, subscribeId);
200         } else {
201             mWifiAwareStateManager.onSessionConfigFailResponse(id, false, status);
202         }
203     }
204 
205     @Override
notifyTransmitFollowupResponse(short id, int status)206     public void notifyTransmitFollowupResponse(short id, int status) {
207         if (status == NanStatusCode.SUCCESS) {
208             mWifiAwareStateManager.onMessageSendQueuedSuccessResponse(id);
209         } else {
210             mWifiAwareStateManager.onMessageSendQueuedFailResponse(id, status);
211         }
212     }
213 
214     @Override
notifyCreateDataInterfaceResponse(short id, int status)215     public void notifyCreateDataInterfaceResponse(short id, int status) {
216         mWifiAwareStateManager.onCreateDataPathInterfaceResponse(id,
217                 status == NanStatusCode.SUCCESS, status);
218     }
219 
220     @Override
notifyDeleteDataInterfaceResponse(short id, int status)221     public void notifyDeleteDataInterfaceResponse(short id, int status) {
222         mWifiAwareStateManager.onDeleteDataPathInterfaceResponse(id,
223                 status == NanStatusCode.SUCCESS, status);
224     }
225 
226     @Override
notifyInitiateDataPathResponse(short id, int status, int ndpInstanceId)227     public void notifyInitiateDataPathResponse(short id, int status,
228             int ndpInstanceId) {
229         if (status == NanStatusCode.SUCCESS) {
230             mWifiAwareStateManager.onInitiateDataPathResponseSuccess(id, ndpInstanceId);
231         } else {
232             mWifiAwareStateManager.onInitiateDataPathResponseFail(id, status);
233         }
234     }
235 
236     @Override
notifyRespondToDataPathIndicationResponse(short id, int status)237     public void notifyRespondToDataPathIndicationResponse(short id, int status) {
238         mWifiAwareStateManager.onRespondToDataPathSetupRequestResponse(id,
239                 status == NanStatusCode.SUCCESS, status);
240     }
241 
242     @Override
notifyTerminateDataPathResponse(short id, int status)243     public void notifyTerminateDataPathResponse(short id, int status) {
244         mWifiAwareStateManager.onEndDataPathResponse(id, status == NanStatusCode.SUCCESS,
245                 status);
246     }
247 
248     @Override
notifyInitiatePairingResponse(short id, int status, int pairingInstanceId)249     public void notifyInitiatePairingResponse(short id, int status,
250             int pairingInstanceId) {
251         if (status == NanStatusCode.SUCCESS) {
252             mWifiAwareStateManager.onInitiatePairingResponseSuccess(id, pairingInstanceId);
253         } else {
254             mWifiAwareStateManager.onInitiatePairingResponseFail(id, status);
255         }
256 
257     }
258 
259     @Override
notifyRespondToPairingIndicationResponse(short id, int status)260     public void notifyRespondToPairingIndicationResponse(short id, int status) {
261         if (status == NanStatusCode.SUCCESS) {
262             mWifiAwareStateManager.onRespondToPairingIndicationResponseSuccess(id);
263         } else {
264             mWifiAwareStateManager.onRespondToPairingIndicationResponseFail(id, status);
265         }
266     }
267 
268     @Override
notifyInitiateBootstrappingResponse(short id, int status, int bootstrappingInstanceId)269     public void notifyInitiateBootstrappingResponse(short id, int status,
270             int bootstrappingInstanceId) {
271         if (status == NanStatusCode.SUCCESS) {
272             mWifiAwareStateManager.onInitiateBootStrappingResponseSuccess(id,
273                     bootstrappingInstanceId);
274         } else {
275             mWifiAwareStateManager.onInitiateBootStrappingResponseFail(id, status);
276         }
277     }
278 
279     @Override
notifyRespondToBootstrappingIndicationResponse(short id, int status)280     public void notifyRespondToBootstrappingIndicationResponse(short id, int status) {
281         if (status == NanStatusCode.SUCCESS) {
282             mWifiAwareStateManager.onRespondToBootstrappingIndicationResponseSuccess(id);
283         } else {
284             mWifiAwareStateManager.onRespondToBootstrappingIndicationResponseFail(id, status);
285         }
286     }
287 
288     @Override
notifySuspendResponse(short id, int status)289     public void notifySuspendResponse(short id, int status) {
290         mWifiAwareStateManager.onSuspendResponse(id, status);
291     }
292 
293     @Override
notifyResumeResponse(short id, int status)294     public void notifyResumeResponse(short id, int status) {
295         mWifiAwareStateManager.onResumeResponse(id, status);
296     }
297 
298     @Override
notifyTerminatePairingResponse(short id, int status)299     public void notifyTerminatePairingResponse(short id, int status) {
300         mWifiAwareStateManager.onEndPairingResponse(id, status == NanStatusCode.SUCCESS,
301                 status);
302     }
303 
304     @Override
eventClusterEvent(int eventType, byte[] addr)305     public void eventClusterEvent(int eventType, byte[] addr) {
306         incrementCbCount(CB_EV_CLUSTER);
307         if (eventType == NanClusterEventType.DISCOVERY_MAC_ADDRESS_CHANGED) {
308             mWifiAwareStateManager.onInterfaceAddressChangeNotification(addr);
309         } else if (eventType == NanClusterEventType.STARTED_CLUSTER) {
310             mWifiAwareStateManager.onClusterChangeNotification(
311                     IdentityChangedListener.CLUSTER_CHANGE_EVENT_STARTED, addr);
312         } else if (eventType == NanClusterEventType.JOINED_CLUSTER) {
313             mWifiAwareStateManager.onClusterChangeNotification(
314                     IdentityChangedListener.CLUSTER_CHANGE_EVENT_JOINED, addr);
315         } else {
316             Log.e(TAG, "eventClusterEvent: invalid eventType=" + eventType);
317         }
318     }
319 
320     @Override
eventDisabled(int status)321     public void eventDisabled(int status) {
322         incrementCbCount(CB_EV_DISABLED);
323         mWifiAwareStateManager.onAwareDownNotification(status);
324     }
325 
326     @Override
eventPublishTerminated(byte sessionId, int status)327     public void eventPublishTerminated(byte sessionId, int status) {
328         incrementCbCount(CB_EV_PUBLISH_TERMINATED);
329         mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status, true);
330     }
331 
332     @Override
eventSubscribeTerminated(byte sessionId, int status)333     public void eventSubscribeTerminated(byte sessionId, int status) {
334         incrementCbCount(CB_EV_SUBSCRIBE_TERMINATED);
335         mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status, false);
336     }
337 
338     @Override
eventMatch(byte discoverySessionId, int peerId, byte[] addr, byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndicationType, int rangingMeasurementInMm, byte[] scid, int peerCipherType, byte[] nonce, byte[] tag, AwarePairingConfig pairingConfig, @Nullable List<OuiKeyedData> vendorData)339     public void eventMatch(byte discoverySessionId, int peerId, byte[] addr,
340             byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndicationType,
341             int rangingMeasurementInMm, byte[] scid, int peerCipherType, byte[] nonce, byte[] tag,
342             AwarePairingConfig pairingConfig, @Nullable List<OuiKeyedData> vendorData) {
343         incrementCbCount(CB_EV_MATCH);
344         mWifiAwareStateManager.onMatchNotification(discoverySessionId, peerId,
345                 addr, serviceSpecificInfo, matchFilter, rangingIndicationType,
346                 rangingMeasurementInMm, scid, peerCipherType, nonce, tag, pairingConfig,
347                 vendorData);
348     }
349 
350     @Override
eventMatchExpired(byte discoverySessionId, int peerId)351     public void eventMatchExpired(byte discoverySessionId, int peerId) {
352         incrementCbCount(CB_EV_MATCH_EXPIRED);
353         mWifiAwareStateManager.onMatchExpiredNotification(discoverySessionId, peerId);
354     }
355 
356     @Override
eventFollowupReceived(byte discoverySessionId, int peerId, byte[] addr, byte[] serviceSpecificInfo)357     public void eventFollowupReceived(byte discoverySessionId, int peerId, byte[] addr,
358             byte[] serviceSpecificInfo) {
359         incrementCbCount(CB_EV_FOLLOWUP_RECEIVED);
360         mWifiAwareStateManager.onMessageReceivedNotification(discoverySessionId, peerId,
361                 addr, serviceSpecificInfo);
362     }
363 
364     @Override
eventTransmitFollowup(short id, int status)365     public void eventTransmitFollowup(short id, int status) {
366         incrementCbCount(CB_EV_TRANSMIT_FOLLOWUP);
367         if (status == NanStatusCode.SUCCESS) {
368             mWifiAwareStateManager.onMessageSendSuccessNotification(id);
369         } else {
370             mWifiAwareStateManager.onMessageSendFailNotification(id, status);
371         }
372     }
373 
374     @Override
eventDataPathRequest(byte discoverySessionId, byte[] peerDiscMacAddr, int ndpInstanceId, byte[] appInfo)375     public void eventDataPathRequest(byte discoverySessionId, byte[] peerDiscMacAddr,
376             int ndpInstanceId, byte[] appInfo) {
377         incrementCbCount(CB_EV_DATA_PATH_REQUEST);
378         mWifiAwareStateManager.onDataPathRequestNotification(discoverySessionId,
379                 peerDiscMacAddr, ndpInstanceId, appInfo);
380     }
381 
382     @Override
eventDataPathConfirm(int status, int ndpInstanceId, boolean dataPathSetupSuccess, byte[] peerNdiMacAddr, byte[] appInfo, List<WifiAwareChannelInfo> channelInfos)383     public void eventDataPathConfirm(int status, int ndpInstanceId, boolean dataPathSetupSuccess,
384             byte[] peerNdiMacAddr, byte[] appInfo,
385             List<WifiAwareChannelInfo> channelInfos) {
386         incrementCbCount(CB_EV_DATA_PATH_CONFIRM);
387         if (channelInfos != null) {
388             mChannelInfoPerNdp.put(ndpInstanceId, channelInfos);
389         }
390         mWifiAwareStateManager.onDataPathConfirmNotification(ndpInstanceId,
391                 peerNdiMacAddr, dataPathSetupSuccess, status, appInfo, channelInfos);
392     }
393 
394     @Override
eventDataPathScheduleUpdate(byte[] peerDiscoveryAddress, ArrayList<Integer> ndpInstanceIds, List<WifiAwareChannelInfo> channelInfo)395     public void eventDataPathScheduleUpdate(byte[] peerDiscoveryAddress,
396             ArrayList<Integer> ndpInstanceIds, List<WifiAwareChannelInfo> channelInfo) {
397         incrementCbCount(CB_EV_DATA_PATH_SCHED_UPDATE);
398         for (int ndpInstanceId : ndpInstanceIds) {
399             mChannelInfoPerNdp.put(ndpInstanceId, channelInfo);
400         }
401         mWifiAwareStateManager.onDataPathScheduleUpdateNotification(peerDiscoveryAddress,
402                 ndpInstanceIds, channelInfo);
403     }
404 
405     @Override
eventDataPathTerminated(int ndpInstanceId)406     public void eventDataPathTerminated(int ndpInstanceId) {
407         incrementCbCount(CB_EV_DATA_PATH_TERMINATED);
408         mChannelInfoPerNdp.remove(ndpInstanceId);
409         mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId);
410     }
411 
412     @Override
eventPairingRequest(int discoverySessionId, int peerId, byte[] peerDiscMacAddr, int ndpInstanceId, int requestType, boolean enableCache, byte[] nonce, byte[] tag)413     public void eventPairingRequest(int discoverySessionId, int peerId, byte[] peerDiscMacAddr,
414             int ndpInstanceId, int requestType, boolean enableCache, byte[] nonce, byte[] tag) {
415         mWifiAwareStateManager.onPairingRequestNotification(discoverySessionId, peerId,
416                 peerDiscMacAddr, ndpInstanceId, requestType, enableCache, nonce, tag);
417     }
418 
419     @Override
eventPairingConfirm(int pairingId, boolean accept, int reason, int requestType, boolean enableCache, PairingConfigManager.PairingSecurityAssociationInfo npksa)420     public void eventPairingConfirm(int pairingId, boolean accept, int reason, int requestType,
421             boolean enableCache,
422             PairingConfigManager.PairingSecurityAssociationInfo npksa) {
423         mWifiAwareStateManager.onPairingConfirmNotification(pairingId, accept, reason, requestType,
424                 enableCache, npksa);
425     }
426 
427     @Override
eventBootstrappingRequest(int discoverySessionId, int peerId, byte[] peerDiscMacAddr, int bootstrappingInstanceId, int method)428     public void eventBootstrappingRequest(int discoverySessionId, int peerId,
429             byte[] peerDiscMacAddr, int bootstrappingInstanceId, int method) {
430         mWifiAwareStateManager.onBootstrappingRequestNotification(discoverySessionId, peerId,
431                 peerDiscMacAddr, bootstrappingInstanceId, method);
432     }
433 
434     @Override
eventBootstrappingConfirm(int bootstrappingId, int responseCode, int reason, int comebackDelay, byte[] cookie)435     public void eventBootstrappingConfirm(int bootstrappingId, int responseCode, int reason,
436             int comebackDelay, byte[] cookie) {
437         mWifiAwareStateManager.onBootstrappingConfirmNotification(bootstrappingId, responseCode,
438                 reason, comebackDelay, cookie);
439     }
440 
441     @Override
eventSuspensionModeChanged(boolean isSuspended)442     public void eventSuspensionModeChanged(boolean isSuspended) {
443         mWifiAwareStateManager.onSuspensionModeChangedNotification(isSuspended);
444     }
445 
446     /**
447      * Reset the channel info when Aware is down.
448      */
resetChannelInfo()449     /* package */ void resetChannelInfo() {
450         mChannelInfoPerNdp.clear();
451     }
452 
453     /**
454      * Dump the internal state of the class.
455      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)456     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
457         pw.println("WifiAwareNativeCallback:");
458         pw.println("  mCallbackCounter: " + mCallbackCounter);
459         pw.println("  mChannelInfoPerNdp: " + mChannelInfoPerNdp);
460     }
461 
462 
463     // utilities
464 
465     /**
466      * Transfer the channel Info dict into a Json String which can be decoded by Json reader.
467      * The Format is: "{ndpInstanceId: [{"channelFreq": channelFreq,
468      * "channelBandwidth": channelBandwidth, "numSpatialStreams": numSpatialStreams}]}"
469      * @return Json String.
470      */
convertChannelInfoToJsonString()471     private String convertChannelInfoToJsonString() {
472         JSONObject channelInfoJson = new JSONObject();
473         try {
474             for (int i = 0; i < mChannelInfoPerNdp.size(); i++) {
475                 JSONArray infoJsonArray = new JSONArray();
476                 for (WifiAwareChannelInfo info : mChannelInfoPerNdp.valueAt(i)) {
477                     JSONObject j = new JSONObject();
478                     j.put("channelFreq", info.getChannelFrequencyMhz());
479                     j.put("channelBandwidth", info.getChannelBandwidth());
480                     j.put("numSpatialStreams", info.getSpatialStreamCount());
481                     infoJsonArray.put(j);
482                 }
483                 channelInfoJson.put(Integer.toString(mChannelInfoPerNdp.keyAt(i)), infoJsonArray);
484             }
485         } catch (JSONException e) {
486             Log.e(TAG, "onCommand: get_channel_info e=" + e);
487         }
488         return channelInfoJson.toString();
489     }
490 }
491