WifiAwareNativeCallback.java revision 9572e8b6de6cb7912df530ae2376452bf1469b33
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
17package com.android.server.wifi.aware;
18
19import android.hardware.wifi.V1_0.IWifiNanIfaceEventCallback;
20import android.hardware.wifi.V1_0.NanCapabilities;
21import android.hardware.wifi.V1_0.NanClusterEventInd;
22import android.hardware.wifi.V1_0.NanClusterEventType;
23import android.hardware.wifi.V1_0.NanDataPathConfirmInd;
24import android.hardware.wifi.V1_0.NanDataPathRequestInd;
25import android.hardware.wifi.V1_0.NanFollowupReceivedInd;
26import android.hardware.wifi.V1_0.NanMatchInd;
27import android.hardware.wifi.V1_0.NanStatusType;
28import android.hardware.wifi.V1_0.WifiNanStatus;
29import android.os.ShellCommand;
30import android.util.Log;
31import android.util.SparseIntArray;
32
33import libcore.util.HexEncoding;
34
35import org.json.JSONException;
36import org.json.JSONObject;
37
38import java.io.FileDescriptor;
39import java.io.PrintWriter;
40import java.util.ArrayList;
41import java.util.Arrays;
42
43/**
44 * Manages the callbacks from Wi-Fi Aware HIDL (HAL).
45 */
46public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub implements
47        WifiAwareShellCommand.DelegatedShellCommand {
48    private static final String TAG = "WifiAwareNativeCallback";
49    private static final boolean DBG = false;
50    private static final boolean VDBG = false;
51
52    private final WifiAwareStateManager mWifiAwareStateManager;
53
54    public WifiAwareNativeCallback(WifiAwareStateManager wifiAwareStateManager) {
55        mWifiAwareStateManager = wifiAwareStateManager;
56    }
57
58    /*
59     * Counts of callbacks from HAL. Retrievable through shell command.
60     */
61    private static final int CB_EV_CLUSTER = 0;
62    private static final int CB_EV_DISABLED = 1;
63    private static final int CB_EV_PUBLISH_TERMINATED = 2;
64    private static final int CB_EV_SUBSCRIBE_TERMINATED = 3;
65    private static final int CB_EV_MATCH = 4;
66    private static final int CB_EV_MATCH_EXPIRED = 5;
67    private static final int CB_EV_FOLLOWUP_RECEIVED = 6;
68    private static final int CB_EV_TRANSMIT_FOLLOWUP = 7;
69    private static final int CB_EV_DATA_PATH_REQUEST = 8;
70    private static final int CB_EV_DATA_PATH_CONFIRM = 9;
71    private static final int CB_EV_DATA_PATH_TERMINATED = 10;
72
73    private SparseIntArray mCallbackCounter = new SparseIntArray();
74
75    private void incrementCbCount(int callbackId) {
76        mCallbackCounter.put(callbackId, mCallbackCounter.get(callbackId) + 1);
77    }
78
79    /**
80     * Interpreter of adb shell command 'adb shell wifiaware native_cb ...'.
81     *
82     * @return -1 if parameter not recognized or invalid value, 0 otherwise.
83     */
84    @Override
85    public int onCommand(ShellCommand parentShell) {
86        final PrintWriter pwe = parentShell.getErrPrintWriter();
87        final PrintWriter pwo = parentShell.getOutPrintWriter();
88
89        String subCmd = parentShell.getNextArgRequired();
90        if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'");
91        switch (subCmd) {
92            case "get_cb_count": {
93                String option = parentShell.getNextOption();
94                Log.v(TAG, "option='" + option + "'");
95                boolean reset = false;
96                if (option != null) {
97                    if ("--reset".equals(option)) {
98                        reset = true;
99                    } else {
100                        pwe.println("Unknown option to 'get_cb_count'");
101                        return -1;
102                    }
103                }
104
105                JSONObject j = new JSONObject();
106                try {
107                    for (int i = 0; i < mCallbackCounter.size(); ++i) {
108                        j.put(Integer.toString(mCallbackCounter.keyAt(i)),
109                                mCallbackCounter.valueAt(i));
110                    }
111                } catch (JSONException e) {
112                    Log.e(TAG, "onCommand: get_cb_count e=" + e);
113                }
114                pwo.println(j.toString());
115                if (reset) {
116                    mCallbackCounter.clear();
117                }
118                return 0;
119            }
120            default:
121                pwe.println("Unknown 'wifiaware native_cb <cmd>'");
122        }
123
124        return -1;
125    }
126
127    @Override
128    public void onReset() {
129        // NOP (onReset is intended for configuration reset - not data reset)
130    }
131
132    @Override
133    public void onHelp(String command, ShellCommand parentShell) {
134        final PrintWriter pw = parentShell.getOutPrintWriter();
135
136        pw.println("  " + command);
137        pw.println("    get_cb_count [--reset]: gets the number of callbacks (and optionally reset "
138                + "count)");
139    }
140
141    @Override
142    public void notifyCapabilitiesResponse(short id, WifiNanStatus status,
143            NanCapabilities capabilities) {
144        if (VDBG) {
145            Log.v(TAG, "notifyCapabilitiesResponse: id=" + id + ", status=" + statusString(status)
146                    + ", capabilities=" + capabilities);
147        }
148
149        if (status.status == NanStatusType.SUCCESS) {
150            Capabilities frameworkCapabilities = new Capabilities();
151            frameworkCapabilities.maxConcurrentAwareClusters = capabilities.maxConcurrentClusters;
152            frameworkCapabilities.maxPublishes = capabilities.maxPublishes;
153            frameworkCapabilities.maxSubscribes = capabilities.maxSubscribes;
154            frameworkCapabilities.maxServiceNameLen = capabilities.maxServiceNameLen;
155            frameworkCapabilities.maxMatchFilterLen = capabilities.maxMatchFilterLen;
156            frameworkCapabilities.maxTotalMatchFilterLen = capabilities.maxTotalMatchFilterLen;
157            frameworkCapabilities.maxServiceSpecificInfoLen =
158                    capabilities.maxServiceSpecificInfoLen;
159            frameworkCapabilities.maxExtendedServiceSpecificInfoLen =
160                    capabilities.maxExtendedServiceSpecificInfoLen;
161            frameworkCapabilities.maxNdiInterfaces = capabilities.maxNdiInterfaces;
162            frameworkCapabilities.maxNdpSessions = capabilities.maxNdpSessions;
163            frameworkCapabilities.maxAppInfoLen = capabilities.maxAppInfoLen;
164            frameworkCapabilities.maxQueuedTransmitMessages =
165                    capabilities.maxQueuedTransmitFollowupMsgs;
166            frameworkCapabilities.maxSubscribeInterfaceAddresses =
167                    capabilities.maxSubscribeInterfaceAddresses;
168            frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites;
169
170            mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities);
171        } else {
172            Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " ("
173                    + status.description + ")");
174        }
175    }
176
177    @Override
178    public void notifyEnableResponse(short id, WifiNanStatus status) {
179        if (VDBG) Log.v(TAG, "notifyEnableResponse: id=" + id + ", status=" + statusString(status));
180
181        if (status.status == NanStatusType.SUCCESS) {
182            mWifiAwareStateManager.onConfigSuccessResponse(id);
183        } else {
184            mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
185        }
186    }
187
188    @Override
189    public void notifyConfigResponse(short id, WifiNanStatus status) {
190        if (VDBG) Log.v(TAG, "notifyConfigResponse: id=" + id + ", status=" + statusString(status));
191
192        if (status.status == NanStatusType.SUCCESS) {
193            mWifiAwareStateManager.onConfigSuccessResponse(id);
194        } else {
195            mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
196        }
197    }
198
199    @Override
200    public void notifyDisableResponse(short id, WifiNanStatus status) {
201        if (VDBG) {
202            Log.v(TAG, "notifyDisableResponse: id=" + id + ", status=" + statusString(status));
203        }
204
205        if (status.status != NanStatusType.SUCCESS) {
206            Log.e(TAG, "notifyDisableResponse: failure - code=" + status.status + " ("
207                    + status.description + ")");
208        }
209        mWifiAwareStateManager.onDisableResponse(id, status.status);
210    }
211
212    @Override
213    public void notifyStartPublishResponse(short id, WifiNanStatus status, byte publishId) {
214        if (VDBG) {
215            Log.v(TAG, "notifyStartPublishResponse: id=" + id + ", status=" + statusString(status)
216                    + ", publishId=" + publishId);
217        }
218
219        if (status.status == NanStatusType.SUCCESS) {
220            mWifiAwareStateManager.onSessionConfigSuccessResponse(id, true, publishId);
221        } else {
222            mWifiAwareStateManager.onSessionConfigFailResponse(id, true, status.status);
223        }
224    }
225
226    @Override
227    public void notifyStopPublishResponse(short id, WifiNanStatus status) {
228        if (VDBG) {
229            Log.v(TAG, "notifyStopPublishResponse: id=" + id + ", status=" + statusString(status));
230        }
231
232        if (status.status == NanStatusType.SUCCESS) {
233            // NOP
234        } else {
235            Log.e(TAG, "notifyStopPublishResponse: failure - code=" + status.status + " ("
236                    + status.description + ")");
237        }
238    }
239
240    @Override
241    public void notifyStartSubscribeResponse(short id, WifiNanStatus status, byte subscribeId) {
242        if (VDBG) {
243            Log.v(TAG, "notifyStartSubscribeResponse: id=" + id + ", status=" + statusString(status)
244                    + ", subscribeId=" + subscribeId);
245        }
246
247        if (status.status == NanStatusType.SUCCESS) {
248            mWifiAwareStateManager.onSessionConfigSuccessResponse(id, false, subscribeId);
249        } else {
250            mWifiAwareStateManager.onSessionConfigFailResponse(id, false, status.status);
251        }
252    }
253
254    @Override
255    public void notifyStopSubscribeResponse(short id, WifiNanStatus status) {
256        if (VDBG) {
257            Log.v(TAG, "notifyStopSubscribeResponse: id=" + id + ", status="
258                    + statusString(status));
259        }
260
261        if (status.status == NanStatusType.SUCCESS) {
262            // NOP
263        } else {
264            Log.e(TAG, "notifyStopSubscribeResponse: failure - code=" + status.status + " ("
265                    + status.description + ")");
266        }
267    }
268
269    @Override
270    public void notifyTransmitFollowupResponse(short id, WifiNanStatus status) {
271        if (VDBG) {
272            Log.v(TAG, "notifyTransmitFollowupResponse: id=" + id + ", status="
273                    + statusString(status));
274        }
275
276        if (status.status == NanStatusType.SUCCESS) {
277            mWifiAwareStateManager.onMessageSendQueuedSuccessResponse(id);
278        } else {
279            mWifiAwareStateManager.onMessageSendQueuedFailResponse(id, status.status);
280        }
281    }
282
283    @Override
284    public void notifyCreateDataInterfaceResponse(short id, WifiNanStatus status) {
285        if (VDBG) {
286            Log.v(TAG, "notifyCreateDataInterfaceResponse: id=" + id + ", status="
287                    + statusString(status));
288        }
289
290        mWifiAwareStateManager.onCreateDataPathInterfaceResponse(id,
291                status.status == NanStatusType.SUCCESS, status.status);
292    }
293
294    @Override
295    public void notifyDeleteDataInterfaceResponse(short id, WifiNanStatus status) {
296        if (VDBG) {
297            Log.v(TAG, "notifyDeleteDataInterfaceResponse: id=" + id + ", status="
298                    + statusString(status));
299        }
300
301        mWifiAwareStateManager.onDeleteDataPathInterfaceResponse(id,
302                status.status == NanStatusType.SUCCESS, status.status);
303    }
304
305    @Override
306    public void notifyInitiateDataPathResponse(short id, WifiNanStatus status,
307            int ndpInstanceId) {
308        if (VDBG) {
309            Log.v(TAG, "notifyInitiateDataPathResponse: id=" + id + ", status="
310                    + statusString(status) + ", ndpInstanceId=" + ndpInstanceId);
311        }
312
313        if (status.status == NanStatusType.SUCCESS) {
314            mWifiAwareStateManager.onInitiateDataPathResponseSuccess(id, ndpInstanceId);
315        } else {
316            mWifiAwareStateManager.onInitiateDataPathResponseFail(id, status.status);
317        }
318    }
319
320    @Override
321    public void notifyRespondToDataPathIndicationResponse(short id, WifiNanStatus status) {
322        if (VDBG) {
323            Log.v(TAG, "notifyRespondToDataPathIndicationResponse: id=" + id
324                    + ", status=" + statusString(status));
325        }
326
327        mWifiAwareStateManager.onRespondToDataPathSetupRequestResponse(id,
328                status.status == NanStatusType.SUCCESS, status.status);
329    }
330
331    @Override
332    public void notifyTerminateDataPathResponse(short id, WifiNanStatus status) {
333        if (VDBG) {
334            Log.v(TAG, "notifyTerminateDataPathResponse: id=" + id + ", status="
335                    + statusString(status));
336        }
337
338        mWifiAwareStateManager.onEndDataPathResponse(id, status.status == NanStatusType.SUCCESS,
339                status.status);
340    }
341
342    @Override
343    public void eventClusterEvent(NanClusterEventInd event) {
344        if (VDBG) {
345            Log.v(TAG, "eventClusterEvent: eventType=" + event.eventType + ", addr="
346                    + String.valueOf(HexEncoding.encode(event.addr)));
347        }
348        incrementCbCount(CB_EV_CLUSTER);
349
350        if (event.eventType == NanClusterEventType.DISCOVERY_MAC_ADDRESS_CHANGED) {
351            mWifiAwareStateManager.onInterfaceAddressChangeNotification(event.addr);
352        } else if (event.eventType == NanClusterEventType.STARTED_CLUSTER) {
353            mWifiAwareStateManager.onClusterChangeNotification(
354                    WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED, event.addr);
355        } else if (event.eventType == NanClusterEventType.JOINED_CLUSTER) {
356            mWifiAwareStateManager.onClusterChangeNotification(
357                    WifiAwareClientState.CLUSTER_CHANGE_EVENT_JOINED, event.addr);
358        } else {
359            Log.e(TAG, "eventClusterEvent: invalid eventType=" + event.eventType);
360        }
361    }
362
363    @Override
364    public void eventDisabled(WifiNanStatus status) {
365        if (VDBG) Log.v(TAG, "eventDisabled: status=" + statusString(status));
366        incrementCbCount(CB_EV_DISABLED);
367
368        mWifiAwareStateManager.onAwareDownNotification(status.status);
369    }
370
371    @Override
372    public void eventPublishTerminated(byte sessionId, WifiNanStatus status) {
373        if (VDBG) {
374            Log.v(TAG, "eventPublishTerminated: sessionId=" + sessionId + ", status="
375                    + statusString(status));
376        }
377        incrementCbCount(CB_EV_PUBLISH_TERMINATED);
378
379        mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, true);
380    }
381
382    @Override
383    public void eventSubscribeTerminated(byte sessionId, WifiNanStatus status) {
384        if (VDBG) {
385            Log.v(TAG, "eventSubscribeTerminated: sessionId=" + sessionId + ", status="
386                    + statusString(status));
387        }
388        incrementCbCount(CB_EV_SUBSCRIBE_TERMINATED);
389
390        mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, false);
391    }
392
393    @Override
394    public void eventMatch(NanMatchInd event) {
395        if (VDBG) {
396            Log.v(TAG, "eventMatch: discoverySessionId=" + event.discoverySessionId + ", peerId="
397                    + event.peerId + ", addr=" + String.valueOf(HexEncoding.encode(event.addr))
398                    + ", serviceSpecificInfo=" + Arrays.toString(
399                    convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()="
400                    + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size())
401                    + ", matchFilter=" + Arrays.toString(
402                    convertArrayListToNativeByteArray(event.matchFilter)) + ", mf.size()=" + (
403                    event.matchFilter == null ? 0 : event.matchFilter.size()));
404        }
405        incrementCbCount(CB_EV_MATCH);
406
407        mWifiAwareStateManager.onMatchNotification(event.discoverySessionId, event.peerId,
408                event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo),
409                convertArrayListToNativeByteArray(event.matchFilter));
410    }
411
412    @Override
413    public void eventMatchExpired(byte discoverySessionId, int peerId) {
414        if (VDBG) {
415            Log.v(TAG, "eventMatchExpired: discoverySessionId=" + discoverySessionId
416                    + ", peerId=" + peerId);
417        }
418        incrementCbCount(CB_EV_MATCH_EXPIRED);
419
420        // NOP
421    }
422
423    @Override
424    public void eventFollowupReceived(NanFollowupReceivedInd event) {
425        if (VDBG) {
426            Log.v(TAG, "eventFollowupReceived: discoverySessionId=" + event.discoverySessionId
427                    + ", peerId=" + event.peerId + ", addr=" + String.valueOf(
428                    HexEncoding.encode(event.addr)) + ", serviceSpecificInfo=" + Arrays.toString(
429                    convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()="
430                    + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size()));
431        }
432        incrementCbCount(CB_EV_FOLLOWUP_RECEIVED);
433
434        mWifiAwareStateManager.onMessageReceivedNotification(event.discoverySessionId, event.peerId,
435                event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo));
436    }
437
438    @Override
439    public void eventTransmitFollowup(short id, WifiNanStatus status) {
440        if (VDBG) {
441            Log.v(TAG, "eventTransmitFollowup: id=" + id + ", status=" + statusString(status));
442        }
443        incrementCbCount(CB_EV_TRANSMIT_FOLLOWUP);
444
445        if (status.status == NanStatusType.SUCCESS) {
446            mWifiAwareStateManager.onMessageSendSuccessNotification(id);
447        } else {
448            mWifiAwareStateManager.onMessageSendFailNotification(id, status.status);
449        }
450    }
451
452    @Override
453    public void eventDataPathRequest(NanDataPathRequestInd event) {
454        if (VDBG) {
455            Log.v(TAG, "eventDataPathRequest: discoverySessionId=" + event.discoverySessionId
456                    + ", peerDiscMacAddr=" + String.valueOf(
457                    HexEncoding.encode(event.peerDiscMacAddr)) + ", ndpInstanceId="
458                    + event.ndpInstanceId);
459        }
460        incrementCbCount(CB_EV_DATA_PATH_REQUEST);
461
462        mWifiAwareStateManager.onDataPathRequestNotification(event.discoverySessionId,
463                event.peerDiscMacAddr, event.ndpInstanceId);
464    }
465
466    @Override
467    public void eventDataPathConfirm(NanDataPathConfirmInd event) {
468        if (VDBG) {
469            Log.v(TAG, "onDataPathConfirm: ndpInstanceId=" + event.ndpInstanceId
470                    + ", peerNdiMacAddr=" + String.valueOf(HexEncoding.encode(event.peerNdiMacAddr))
471                    + ", dataPathSetupSuccess=" + event.dataPathSetupSuccess + ", reason="
472                    + event.status.status);
473        }
474        incrementCbCount(CB_EV_DATA_PATH_CONFIRM);
475
476        mWifiAwareStateManager.onDataPathConfirmNotification(event.ndpInstanceId,
477                event.peerNdiMacAddr, event.dataPathSetupSuccess, event.status.status,
478                convertArrayListToNativeByteArray(event.appInfo));
479    }
480
481    @Override
482    public void eventDataPathTerminated(int ndpInstanceId) {
483        if (VDBG) Log.v(TAG, "eventDataPathTerminated: ndpInstanceId=" + ndpInstanceId);
484        incrementCbCount(CB_EV_DATA_PATH_TERMINATED);
485
486        mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId);
487    }
488
489    /**
490     * Dump the internal state of the class.
491     */
492    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
493        pw.println("WifiAwareNativeCallback:");
494        pw.println("  mCallbackCounter: " + mCallbackCounter);
495    }
496
497
498    // utilities
499
500    /**
501     * Converts an ArrayList<Byte> to a byte[].
502     *
503     * @param from The input ArrayList<Byte></Byte> to convert from.
504     *
505     * @return A newly allocated byte[].
506     */
507    private byte[] convertArrayListToNativeByteArray(ArrayList<Byte> from) {
508        if (from == null) {
509            return null;
510        }
511
512        byte[] to = new byte[from.size()];
513        for (int i = 0; i < from.size(); ++i) {
514            to[i] = from.get(i);
515        }
516        return to;
517    }
518
519    private static String statusString(WifiNanStatus status) {
520        if (status == null) {
521            return "status=null";
522        }
523        StringBuilder sb = new StringBuilder();
524        sb.append(status.status).append(" (").append(status.description).append(")");
525        return sb.toString();
526    }
527}
528