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.ALREADY_ENABLED) {
182            Log.wtf(TAG, "notifyEnableResponse: id=" + id + ", already enabled!?");
183        }
184
185        if (status.status == NanStatusType.SUCCESS
186                || status.status == NanStatusType.ALREADY_ENABLED) {
187            mWifiAwareStateManager.onConfigSuccessResponse(id);
188        } else {
189            mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
190        }
191    }
192
193    @Override
194    public void notifyConfigResponse(short id, WifiNanStatus status) {
195        if (VDBG) Log.v(TAG, "notifyConfigResponse: id=" + id + ", status=" + statusString(status));
196
197        if (status.status == NanStatusType.SUCCESS) {
198            mWifiAwareStateManager.onConfigSuccessResponse(id);
199        } else {
200            mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
201        }
202    }
203
204    @Override
205    public void notifyDisableResponse(short id, WifiNanStatus status) {
206        if (VDBG) {
207            Log.v(TAG, "notifyDisableResponse: id=" + id + ", status=" + statusString(status));
208        }
209
210        if (status.status != NanStatusType.SUCCESS) {
211            Log.e(TAG, "notifyDisableResponse: failure - code=" + status.status + " ("
212                    + status.description + ")");
213        }
214        mWifiAwareStateManager.onDisableResponse(id, status.status);
215    }
216
217    @Override
218    public void notifyStartPublishResponse(short id, WifiNanStatus status, byte publishId) {
219        if (VDBG) {
220            Log.v(TAG, "notifyStartPublishResponse: id=" + id + ", status=" + statusString(status)
221                    + ", publishId=" + publishId);
222        }
223
224        if (status.status == NanStatusType.SUCCESS) {
225            mWifiAwareStateManager.onSessionConfigSuccessResponse(id, true, publishId);
226        } else {
227            mWifiAwareStateManager.onSessionConfigFailResponse(id, true, status.status);
228        }
229    }
230
231    @Override
232    public void notifyStopPublishResponse(short id, WifiNanStatus status) {
233        if (VDBG) {
234            Log.v(TAG, "notifyStopPublishResponse: id=" + id + ", status=" + statusString(status));
235        }
236
237        if (status.status == NanStatusType.SUCCESS) {
238            // NOP
239        } else {
240            Log.e(TAG, "notifyStopPublishResponse: failure - code=" + status.status + " ("
241                    + status.description + ")");
242        }
243    }
244
245    @Override
246    public void notifyStartSubscribeResponse(short id, WifiNanStatus status, byte subscribeId) {
247        if (VDBG) {
248            Log.v(TAG, "notifyStartSubscribeResponse: id=" + id + ", status=" + statusString(status)
249                    + ", subscribeId=" + subscribeId);
250        }
251
252        if (status.status == NanStatusType.SUCCESS) {
253            mWifiAwareStateManager.onSessionConfigSuccessResponse(id, false, subscribeId);
254        } else {
255            mWifiAwareStateManager.onSessionConfigFailResponse(id, false, status.status);
256        }
257    }
258
259    @Override
260    public void notifyStopSubscribeResponse(short id, WifiNanStatus status) {
261        if (VDBG) {
262            Log.v(TAG, "notifyStopSubscribeResponse: id=" + id + ", status="
263                    + statusString(status));
264        }
265
266        if (status.status == NanStatusType.SUCCESS) {
267            // NOP
268        } else {
269            Log.e(TAG, "notifyStopSubscribeResponse: failure - code=" + status.status + " ("
270                    + status.description + ")");
271        }
272    }
273
274    @Override
275    public void notifyTransmitFollowupResponse(short id, WifiNanStatus status) {
276        if (VDBG) {
277            Log.v(TAG, "notifyTransmitFollowupResponse: id=" + id + ", status="
278                    + statusString(status));
279        }
280
281        if (status.status == NanStatusType.SUCCESS) {
282            mWifiAwareStateManager.onMessageSendQueuedSuccessResponse(id);
283        } else {
284            mWifiAwareStateManager.onMessageSendQueuedFailResponse(id, status.status);
285        }
286    }
287
288    @Override
289    public void notifyCreateDataInterfaceResponse(short id, WifiNanStatus status) {
290        if (VDBG) {
291            Log.v(TAG, "notifyCreateDataInterfaceResponse: id=" + id + ", status="
292                    + statusString(status));
293        }
294
295        mWifiAwareStateManager.onCreateDataPathInterfaceResponse(id,
296                status.status == NanStatusType.SUCCESS, status.status);
297    }
298
299    @Override
300    public void notifyDeleteDataInterfaceResponse(short id, WifiNanStatus status) {
301        if (VDBG) {
302            Log.v(TAG, "notifyDeleteDataInterfaceResponse: id=" + id + ", status="
303                    + statusString(status));
304        }
305
306        mWifiAwareStateManager.onDeleteDataPathInterfaceResponse(id,
307                status.status == NanStatusType.SUCCESS, status.status);
308    }
309
310    @Override
311    public void notifyInitiateDataPathResponse(short id, WifiNanStatus status,
312            int ndpInstanceId) {
313        if (VDBG) {
314            Log.v(TAG, "notifyInitiateDataPathResponse: id=" + id + ", status="
315                    + statusString(status) + ", ndpInstanceId=" + ndpInstanceId);
316        }
317
318        if (status.status == NanStatusType.SUCCESS) {
319            mWifiAwareStateManager.onInitiateDataPathResponseSuccess(id, ndpInstanceId);
320        } else {
321            mWifiAwareStateManager.onInitiateDataPathResponseFail(id, status.status);
322        }
323    }
324
325    @Override
326    public void notifyRespondToDataPathIndicationResponse(short id, WifiNanStatus status) {
327        if (VDBG) {
328            Log.v(TAG, "notifyRespondToDataPathIndicationResponse: id=" + id
329                    + ", status=" + statusString(status));
330        }
331
332        mWifiAwareStateManager.onRespondToDataPathSetupRequestResponse(id,
333                status.status == NanStatusType.SUCCESS, status.status);
334    }
335
336    @Override
337    public void notifyTerminateDataPathResponse(short id, WifiNanStatus status) {
338        if (VDBG) {
339            Log.v(TAG, "notifyTerminateDataPathResponse: id=" + id + ", status="
340                    + statusString(status));
341        }
342
343        mWifiAwareStateManager.onEndDataPathResponse(id, status.status == NanStatusType.SUCCESS,
344                status.status);
345    }
346
347    @Override
348    public void eventClusterEvent(NanClusterEventInd event) {
349        if (VDBG) {
350            Log.v(TAG, "eventClusterEvent: eventType=" + event.eventType + ", addr="
351                    + String.valueOf(HexEncoding.encode(event.addr)));
352        }
353        incrementCbCount(CB_EV_CLUSTER);
354
355        if (event.eventType == NanClusterEventType.DISCOVERY_MAC_ADDRESS_CHANGED) {
356            mWifiAwareStateManager.onInterfaceAddressChangeNotification(event.addr);
357        } else if (event.eventType == NanClusterEventType.STARTED_CLUSTER) {
358            mWifiAwareStateManager.onClusterChangeNotification(
359                    WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED, event.addr);
360        } else if (event.eventType == NanClusterEventType.JOINED_CLUSTER) {
361            mWifiAwareStateManager.onClusterChangeNotification(
362                    WifiAwareClientState.CLUSTER_CHANGE_EVENT_JOINED, event.addr);
363        } else {
364            Log.e(TAG, "eventClusterEvent: invalid eventType=" + event.eventType);
365        }
366    }
367
368    @Override
369    public void eventDisabled(WifiNanStatus status) {
370        if (VDBG) Log.v(TAG, "eventDisabled: status=" + statusString(status));
371        incrementCbCount(CB_EV_DISABLED);
372
373        mWifiAwareStateManager.onAwareDownNotification(status.status);
374    }
375
376    @Override
377    public void eventPublishTerminated(byte sessionId, WifiNanStatus status) {
378        if (VDBG) {
379            Log.v(TAG, "eventPublishTerminated: sessionId=" + sessionId + ", status="
380                    + statusString(status));
381        }
382        incrementCbCount(CB_EV_PUBLISH_TERMINATED);
383
384        mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, true);
385    }
386
387    @Override
388    public void eventSubscribeTerminated(byte sessionId, WifiNanStatus status) {
389        if (VDBG) {
390            Log.v(TAG, "eventSubscribeTerminated: sessionId=" + sessionId + ", status="
391                    + statusString(status));
392        }
393        incrementCbCount(CB_EV_SUBSCRIBE_TERMINATED);
394
395        mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, false);
396    }
397
398    @Override
399    public void eventMatch(NanMatchInd event) {
400        if (VDBG) {
401            Log.v(TAG, "eventMatch: discoverySessionId=" + event.discoverySessionId + ", peerId="
402                    + event.peerId + ", addr=" + String.valueOf(HexEncoding.encode(event.addr))
403                    + ", serviceSpecificInfo=" + Arrays.toString(
404                    convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()="
405                    + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size())
406                    + ", matchFilter=" + Arrays.toString(
407                    convertArrayListToNativeByteArray(event.matchFilter)) + ", mf.size()=" + (
408                    event.matchFilter == null ? 0 : event.matchFilter.size()));
409        }
410        incrementCbCount(CB_EV_MATCH);
411
412        mWifiAwareStateManager.onMatchNotification(event.discoverySessionId, event.peerId,
413                event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo),
414                convertArrayListToNativeByteArray(event.matchFilter));
415    }
416
417    @Override
418    public void eventMatchExpired(byte discoverySessionId, int peerId) {
419        if (VDBG) {
420            Log.v(TAG, "eventMatchExpired: discoverySessionId=" + discoverySessionId
421                    + ", peerId=" + peerId);
422        }
423        incrementCbCount(CB_EV_MATCH_EXPIRED);
424
425        // NOP
426    }
427
428    @Override
429    public void eventFollowupReceived(NanFollowupReceivedInd event) {
430        if (VDBG) {
431            Log.v(TAG, "eventFollowupReceived: discoverySessionId=" + event.discoverySessionId
432                    + ", peerId=" + event.peerId + ", addr=" + String.valueOf(
433                    HexEncoding.encode(event.addr)) + ", serviceSpecificInfo=" + Arrays.toString(
434                    convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()="
435                    + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size()));
436        }
437        incrementCbCount(CB_EV_FOLLOWUP_RECEIVED);
438
439        mWifiAwareStateManager.onMessageReceivedNotification(event.discoverySessionId, event.peerId,
440                event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo));
441    }
442
443    @Override
444    public void eventTransmitFollowup(short id, WifiNanStatus status) {
445        if (VDBG) {
446            Log.v(TAG, "eventTransmitFollowup: id=" + id + ", status=" + statusString(status));
447        }
448        incrementCbCount(CB_EV_TRANSMIT_FOLLOWUP);
449
450        if (status.status == NanStatusType.SUCCESS) {
451            mWifiAwareStateManager.onMessageSendSuccessNotification(id);
452        } else {
453            mWifiAwareStateManager.onMessageSendFailNotification(id, status.status);
454        }
455    }
456
457    @Override
458    public void eventDataPathRequest(NanDataPathRequestInd event) {
459        if (VDBG) {
460            Log.v(TAG, "eventDataPathRequest: discoverySessionId=" + event.discoverySessionId
461                    + ", peerDiscMacAddr=" + String.valueOf(
462                    HexEncoding.encode(event.peerDiscMacAddr)) + ", ndpInstanceId="
463                    + event.ndpInstanceId);
464        }
465        incrementCbCount(CB_EV_DATA_PATH_REQUEST);
466
467        mWifiAwareStateManager.onDataPathRequestNotification(event.discoverySessionId,
468                event.peerDiscMacAddr, event.ndpInstanceId);
469    }
470
471    @Override
472    public void eventDataPathConfirm(NanDataPathConfirmInd event) {
473        if (VDBG) {
474            Log.v(TAG, "onDataPathConfirm: ndpInstanceId=" + event.ndpInstanceId
475                    + ", peerNdiMacAddr=" + String.valueOf(HexEncoding.encode(event.peerNdiMacAddr))
476                    + ", dataPathSetupSuccess=" + event.dataPathSetupSuccess + ", reason="
477                    + event.status.status);
478        }
479        incrementCbCount(CB_EV_DATA_PATH_CONFIRM);
480
481        mWifiAwareStateManager.onDataPathConfirmNotification(event.ndpInstanceId,
482                event.peerNdiMacAddr, event.dataPathSetupSuccess, event.status.status,
483                convertArrayListToNativeByteArray(event.appInfo));
484    }
485
486    @Override
487    public void eventDataPathTerminated(int ndpInstanceId) {
488        if (VDBG) Log.v(TAG, "eventDataPathTerminated: ndpInstanceId=" + ndpInstanceId);
489        incrementCbCount(CB_EV_DATA_PATH_TERMINATED);
490
491        mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId);
492    }
493
494    /**
495     * Dump the internal state of the class.
496     */
497    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
498        pw.println("WifiAwareNativeCallback:");
499        pw.println("  mCallbackCounter: " + mCallbackCounter);
500    }
501
502
503    // utilities
504
505    /**
506     * Converts an ArrayList<Byte> to a byte[].
507     *
508     * @param from The input ArrayList<Byte></Byte> to convert from.
509     *
510     * @return A newly allocated byte[].
511     */
512    private byte[] convertArrayListToNativeByteArray(ArrayList<Byte> from) {
513        if (from == null) {
514            return null;
515        }
516
517        byte[] to = new byte[from.size()];
518        for (int i = 0; i < from.size(); ++i) {
519            to[i] = from.get(i);
520        }
521        return to;
522    }
523
524    private static String statusString(WifiNanStatus status) {
525        if (status == null) {
526            return "status=null";
527        }
528        StringBuilder sb = new StringBuilder();
529        sb.append(status.status).append(" (").append(status.description).append(")");
530        return sb.toString();
531    }
532}
533