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.p2p;
18
19import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback;
20import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
21import android.net.wifi.WpsInfo;
22import android.net.wifi.p2p.WifiP2pConfig;
23import android.net.wifi.p2p.WifiP2pDevice;
24import android.net.wifi.p2p.WifiP2pGroup;
25import android.net.wifi.p2p.WifiP2pProvDiscEvent;
26import android.net.wifi.p2p.WifiP2pWfdInfo;
27import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
28import android.util.Log;
29
30import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus;
31import com.android.server.wifi.util.NativeUtil;
32
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.List;
36
37/**
38 * Class used for processing all P2P callbacks.
39 */
40public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub {
41    private static final String TAG = "SupplicantP2pIfaceCallback";
42    private static final boolean DBG = true;
43
44    private final String mInterface;
45    private final WifiP2pMonitor mMonitor;
46
47    public SupplicantP2pIfaceCallback(String iface, WifiP2pMonitor monitor) {
48        mInterface = iface;
49        mMonitor = monitor;
50    }
51
52
53    protected static void logd(String s) {
54        if (DBG) Log.d(TAG, s);
55    }
56
57    /**
58     * Used to indicate that a new network has been added.
59     *
60     * @param networkId Network ID allocated to the corresponding network.
61     */
62    public void onNetworkAdded(int networkId) {
63    }
64
65
66    /**
67     * Used to indicate that a network has been removed.
68     *
69     * @param networkId Network ID allocated to the corresponding network.
70     */
71    public void onNetworkRemoved(int networkId) {
72    }
73
74
75    /**
76     * Used to indicate that a P2P device has been found.
77     *
78     * @param srcAddress MAC address of the device found. This must either
79     *        be the P2P device address or the P2P interface address.
80     * @param p2pDeviceAddress P2P device address.
81     * @param primaryDeviceType Type of device. Refer to section B.1 of Wifi P2P
82     *        Technical specification v1.2.
83     * @param deviceName Name of the device.
84     * @param configMethods Mask of WPS configuration methods supported by the
85     *        device.
86     * @param deviceCapabilities Refer to section 4.1.4 of Wifi P2P Technical
87     *        specification v1.2.
88     * @param groupCapabilities Refer to section 4.1.4 of Wifi P2P Technical
89     *        specification v1.2.
90     * @param wfdDeviceInfo WFD device info as described in section 5.1.2 of WFD
91     *        technical specification v1.0.0.
92     */
93    public void onDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress, byte[] primaryDeviceType,
94            String deviceName, short configMethods, byte deviceCapabilities, int groupCapabilities,
95            byte[] wfdDeviceInfo) {
96        WifiP2pDevice device = new WifiP2pDevice();
97        device.deviceName = deviceName;
98        if (deviceName == null) {
99            Log.e(TAG, "Missing device name.");
100            return;
101        }
102
103        try {
104            device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
105        } catch (Exception e) {
106            Log.e(TAG, "Could not decode device address.", e);
107            return;
108        }
109
110        try {
111            device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray(primaryDeviceType);
112        } catch (Exception e) {
113            Log.e(TAG, "Could not encode device primary type.", e);
114            return;
115        }
116
117        device.deviceCapability = deviceCapabilities;
118        device.groupCapability = groupCapabilities;
119        device.wpsConfigMethodsSupported = configMethods;
120        device.status = WifiP2pDevice.AVAILABLE;
121
122        if (wfdDeviceInfo != null && wfdDeviceInfo.length >= 6) {
123            device.wfdInfo = new WifiP2pWfdInfo(
124                    (wfdDeviceInfo[0] << 8) + wfdDeviceInfo[1],
125                    (wfdDeviceInfo[2] << 8) + wfdDeviceInfo[3],
126                    (wfdDeviceInfo[4] << 8) + wfdDeviceInfo[5]);
127        }
128
129        logd("Device discovered on " + mInterface + ": " + device);
130        mMonitor.broadcastP2pDeviceFound(mInterface, device);
131    }
132
133    /**
134     * Used to indicate that a P2P device has been lost.
135     *
136     * @param p2pDeviceAddress P2P device address.
137     */
138    public void onDeviceLost(byte[] p2pDeviceAddress) {
139        WifiP2pDevice device = new WifiP2pDevice();
140
141        try {
142            device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
143        } catch (Exception e) {
144            Log.e(TAG, "Could not decode device address.", e);
145            return;
146        }
147
148        device.status = WifiP2pDevice.UNAVAILABLE;
149
150        logd("Device lost on " + mInterface + ": " + device);
151        mMonitor.broadcastP2pDeviceLost(mInterface, device);
152    }
153
154
155    /**
156     * Used to indicate the termination of P2P find operation.
157     */
158    public void onFindStopped() {
159        logd("Search stopped on " + mInterface);
160        mMonitor.broadcastP2pFindStopped(mInterface);
161    }
162
163
164    /**
165     * Used to indicate the reception of a P2P Group Owner negotiation request.
166     *
167     * @param srcAddress MAC address of the device that initiated the GO
168     *        negotiation request.
169     * @param passwordId Type of password.
170     */
171    public void onGoNegotiationRequest(byte[] srcAddress, short passwordId) {
172        WifiP2pConfig config = new WifiP2pConfig();
173
174        try {
175            config.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress);
176        } catch (Exception e) {
177            Log.e(TAG, "Could not decode device address.", e);
178            return;
179        }
180
181        config.wps = new WpsInfo();
182
183        switch (passwordId) {
184            case WpsDevPasswordId.USER_SPECIFIED:
185                config.wps.setup = WpsInfo.DISPLAY;
186                break;
187
188            case WpsDevPasswordId.PUSHBUTTON:
189                config.wps.setup = WpsInfo.PBC;
190                break;
191
192            case WpsDevPasswordId.REGISTRAR_SPECIFIED:
193                config.wps.setup = WpsInfo.KEYPAD;
194                break;
195
196            default:
197                config.wps.setup = WpsInfo.PBC;
198                break;
199        }
200
201        logd("Group Owner negotiation initiated on " + mInterface + ": " + config);
202        mMonitor.broadcastP2pGoNegotiationRequest(mInterface, config);
203    }
204
205
206    /**
207     * Used to indicate the completion of a P2P Group Owner negotiation request.
208     *
209     * @param status Status of the GO negotiation.
210     */
211    public void onGoNegotiationCompleted(int status) {
212        logd("Group Owner negotiation completed with status: " + status);
213        P2pStatus result = halStatusToP2pStatus(status);
214
215        if (result == P2pStatus.SUCCESS) {
216            mMonitor.broadcastP2pGoNegotiationSuccess(mInterface);
217        } else {
218            mMonitor.broadcastP2pGoNegotiationFailure(mInterface, result);
219        }
220    }
221
222
223    /**
224     * Used to indicate a successful formation of a P2P group.
225     */
226    public void onGroupFormationSuccess() {
227        logd("Group formation successful on " + mInterface);
228        mMonitor.broadcastP2pGroupFormationSuccess(mInterface);
229    }
230
231
232    /**
233     * Used to indicate a failure to form a P2P group.
234     *
235     * @param failureReason Failure reason string for debug purposes.
236     */
237    public void onGroupFormationFailure(String failureReason) {
238        // TODO(ender): failureReason should probably be an int (P2pStatusCode).
239        logd("Group formation failed on " + mInterface + ": " + failureReason);
240        mMonitor.broadcastP2pGroupFormationFailure(mInterface, failureReason);
241    }
242
243
244    /**
245     * Used to indicate the start of a P2P group.
246     *
247     * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1)
248     * @param isGo Whether this device is owner of the group.
249     * @param ssid SSID of the group.
250     * @param frequency Frequency on which this group is created.
251     * @param psk PSK used to secure the group.
252     * @param passphrase PSK passphrase used to secure the group.
253     * @param goDeviceAddress MAC Address of the owner of this group.
254     * @param isPersistent Whether this group is persisted or not.
255     */
256    public void onGroupStarted(String groupIfName, boolean isGo, ArrayList<Byte> ssid,
257            int frequency, byte[] psk, String passphrase, byte[] goDeviceAddress,
258            boolean isPersistent) {
259        if (groupIfName == null) {
260            Log.e(TAG, "Missing group interface name.");
261            return;
262        }
263
264        logd("Group " + groupIfName + " started on " + mInterface);
265
266        WifiP2pGroup group = new WifiP2pGroup();
267        group.setInterface(groupIfName);
268
269        try {
270            String quotedSsid = NativeUtil.encodeSsid(ssid);
271            group.setNetworkName(NativeUtil.removeEnclosingQuotes(quotedSsid));
272        } catch (Exception e) {
273            Log.e(TAG, "Could not encode SSID.", e);
274            return;
275        }
276
277        group.setIsGroupOwner(isGo);
278        group.setPassphrase(passphrase);
279
280        if (isPersistent) {
281            group.setNetworkId(WifiP2pGroup.PERSISTENT_NET_ID);
282        } else {
283            group.setNetworkId(WifiP2pGroup.TEMPORARY_NET_ID);
284        }
285
286        WifiP2pDevice owner = new WifiP2pDevice();
287
288        try {
289            owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress);
290        } catch (Exception e) {
291            Log.e(TAG, "Could not decode Group Owner address.", e);
292            return;
293        }
294
295        group.setOwner(owner);
296        mMonitor.broadcastP2pGroupStarted(mInterface, group);
297    }
298
299
300    /**
301     * Used to indicate the removal of a P2P group.
302     *
303     * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1)
304     * @param isGo Whether this device is owner of the group.
305     */
306    public void onGroupRemoved(String groupIfName, boolean isGo) {
307        if (groupIfName == null) {
308            Log.e(TAG, "Missing group name.");
309            return;
310        }
311
312        logd("Group " + groupIfName + " removed from " + mInterface);
313        WifiP2pGroup group = new WifiP2pGroup();
314        group.setInterface(groupIfName);
315        group.setIsGroupOwner(isGo);
316        mMonitor.broadcastP2pGroupRemoved(mInterface, group);
317    }
318
319
320    /**
321     * Used to indicate the reception of a P2P invitation.
322     *
323     * @param srcAddress MAC address of the device that sent the invitation.
324     * @param goDeviceAddress MAC Address of the owner of this group.
325     * @param bssid Bssid of the group.
326     * @param persistentNetworkId Persistent network Id of the group.
327     * @param operatingFrequency Frequency on which the invitation was received.
328     */
329    public void onInvitationReceived(byte[] srcAddress, byte[] goDeviceAddress,
330            byte[] bssid, int persistentNetworkId, int operatingFrequency) {
331        WifiP2pGroup group = new WifiP2pGroup();
332        group.setNetworkId(persistentNetworkId);
333
334        WifiP2pDevice client = new WifiP2pDevice();
335
336        try {
337            client.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress);
338        } catch (Exception e) {
339            Log.e(TAG, "Could not decode MAC address.", e);
340            return;
341        }
342
343        group.addClient(client);
344
345        WifiP2pDevice owner = new WifiP2pDevice();
346
347        try {
348            owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress);
349        } catch (Exception e) {
350            Log.e(TAG, "Could not decode Group Owner MAC address.", e);
351            return;
352        }
353
354        group.setOwner(owner);
355
356        logd("Invitation received on " + mInterface + ": " + group);
357        mMonitor.broadcastP2pInvitationReceived(mInterface, group);
358    }
359
360
361    /**
362     * Used to indicate the result of the P2P invitation request.
363     *
364     * @param bssid Bssid of the group.
365     * @param status Status of the invitation.
366     */
367    public void onInvitationResult(byte[] bssid, int status) {
368        logd("Invitation completed with status: " + status);
369        mMonitor.broadcastP2pInvitationResult(mInterface, halStatusToP2pStatus(status));
370    }
371
372
373    /**
374     * Used to indicate the completion of a P2P provision discovery request.
375     *
376     * @param p2pDeviceAddress P2P device address.
377     * @param isRequest Whether we received or sent the provision discovery.
378     * @param status Status of the provision discovery (SupplicantStatusCode).
379     * @param configMethods Mask of WPS configuration methods supported.
380     *                      Only one configMethod bit should be set per call.
381     * @param generatedPin 8 digit pin generated.
382     */
383    public void onProvisionDiscoveryCompleted(byte[] p2pDeviceAddress, boolean isRequest,
384            byte status, short configMethods, String generatedPin) {
385        if (status != ISupplicantP2pIfaceCallback.P2pProvDiscStatusCode.SUCCESS) {
386            Log.e(TAG, "Provision discovery failed: " + status);
387            mMonitor.broadcastP2pProvisionDiscoveryFailure(mInterface);
388            return;
389        }
390
391        logd("Provision discovery " + (isRequest ? "request" : "response")
392                + " for WPS Config method: " + configMethods);
393
394        WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent();
395        event.device = new WifiP2pDevice();
396
397        try {
398            event.device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
399        } catch (Exception e) {
400            Log.e(TAG, "Could not decode MAC address.", e);
401            return;
402        }
403
404        if ((configMethods & WpsConfigMethods.PUSHBUTTON) != 0) {
405            if (isRequest) {
406                event.event = WifiP2pProvDiscEvent.PBC_REQ;
407                mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event);
408            } else {
409                event.event = WifiP2pProvDiscEvent.PBC_RSP;
410                mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event);
411            }
412        } else if (!isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) {
413            event.event = WifiP2pProvDiscEvent.SHOW_PIN;
414            event.pin = generatedPin;
415            mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event);
416        } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
417            event.event = WifiP2pProvDiscEvent.ENTER_PIN;
418            mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event);
419        } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
420            event.event = WifiP2pProvDiscEvent.SHOW_PIN;
421            event.pin = generatedPin;
422            mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event);
423        } else if (isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) {
424            event.event = WifiP2pProvDiscEvent.ENTER_PIN;
425            mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event);
426        } else {
427            Log.e(TAG, "Unsupported config methods: " + configMethods);
428        }
429    }
430
431
432    /**
433     * Used to indicate the reception of a P2P service discovery response.
434     *
435     * @param srcAddress MAC address of the device that sent the service discovery.
436     * @param updateIndicator Service update indicator. Refer to section 3.1.3 of
437     *        Wifi P2P Technical specification v1.2.
438     * @param tlvs Refer to section 3.1.3.1 of Wifi P2P Technical specification v1.2.
439     */
440    public void onServiceDiscoveryResponse(byte[] srcAddress, short updateIndicator,
441            ArrayList<Byte> tlvs) {
442        List<WifiP2pServiceResponse> response = null;
443
444        logd("Service discovery response received on " + mInterface);
445        try {
446            String srcAddressStr = NativeUtil.macAddressFromByteArray(srcAddress);
447            // updateIndicator is not used
448            response = WifiP2pServiceResponse.newInstance(srcAddressStr,
449                    NativeUtil.byteArrayFromArrayList(tlvs));
450        } catch (Exception e) {
451            Log.e(TAG, "Could not process service discovery response.", e);
452            return;
453        }
454        mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, response);
455    }
456
457    private WifiP2pDevice createStaEventDevice(byte[] srcAddress, byte[] p2pDeviceAddress) {
458        WifiP2pDevice device = new WifiP2pDevice();
459        byte[] deviceAddressBytes;
460        // Legacy STAs may not supply a p2pDeviceAddress (signaled by a zero'd p2pDeviceAddress)
461        // In this case, use srcAddress instead
462        if (!Arrays.equals(NativeUtil.ANY_MAC_BYTES, p2pDeviceAddress)) {
463            deviceAddressBytes = p2pDeviceAddress;
464        } else {
465            deviceAddressBytes = srcAddress;
466        }
467        try {
468            device.deviceAddress = NativeUtil.macAddressFromByteArray(deviceAddressBytes);
469        } catch (Exception e) {
470            Log.e(TAG, "Could not decode MAC address", e);
471            return null;
472        }
473        return device;
474    }
475
476    /**
477     * Used to indicate when a STA device is connected to this device.
478     *
479     * @param srcAddress MAC address of the device that was authorized.
480     * @param p2pDeviceAddress P2P device address.
481     */
482    public void onStaAuthorized(byte[] srcAddress, byte[] p2pDeviceAddress) {
483        logd("STA authorized on " + mInterface);
484        WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress);
485        if (device == null) {
486            return;
487        }
488        mMonitor.broadcastP2pApStaConnected(mInterface, device);
489    }
490
491
492    /**
493     * Used to indicate when a STA device is disconnected from this device.
494     *
495     * @param srcAddress MAC address of the device that was deauthorized.
496     * @param p2pDeviceAddress P2P device address.
497     */
498    public void onStaDeauthorized(byte[] srcAddress, byte[] p2pDeviceAddress) {
499        logd("STA deauthorized on " + mInterface);
500        WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress);
501        if (device == null) {
502            return;
503        }
504        mMonitor.broadcastP2pApStaDisconnected(mInterface, device);
505    }
506
507
508    private static P2pStatus halStatusToP2pStatus(int status) {
509        P2pStatus result = P2pStatus.UNKNOWN;
510
511        switch (status) {
512            case P2pStatusCode.SUCCESS:
513            case P2pStatusCode.SUCCESS_DEFERRED:
514                result = P2pStatus.SUCCESS;
515                break;
516
517            case P2pStatusCode.FAIL_INFO_CURRENTLY_UNAVAILABLE:
518                result = P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE;
519                break;
520
521            case P2pStatusCode.FAIL_INCOMPATIBLE_PARAMS:
522                result = P2pStatus.INCOMPATIBLE_PARAMETERS;
523                break;
524
525            case P2pStatusCode.FAIL_LIMIT_REACHED:
526                result = P2pStatus.LIMIT_REACHED;
527                break;
528
529            case P2pStatusCode.FAIL_INVALID_PARAMS:
530                result = P2pStatus.INVALID_PARAMETER;
531                break;
532
533            case P2pStatusCode.FAIL_UNABLE_TO_ACCOMMODATE:
534                result = P2pStatus.UNABLE_TO_ACCOMMODATE_REQUEST;
535                break;
536
537            case P2pStatusCode.FAIL_PREV_PROTOCOL_ERROR:
538                result = P2pStatus.PREVIOUS_PROTOCOL_ERROR;
539                break;
540
541            case P2pStatusCode.FAIL_NO_COMMON_CHANNELS:
542                result = P2pStatus.NO_COMMON_CHANNEL;
543                break;
544
545            case P2pStatusCode.FAIL_UNKNOWN_GROUP:
546                result = P2pStatus.UNKNOWN_P2P_GROUP;
547                break;
548
549            case P2pStatusCode.FAIL_BOTH_GO_INTENT_15:
550                result = P2pStatus.BOTH_GO_INTENT_15;
551                break;
552
553            case P2pStatusCode.FAIL_INCOMPATIBLE_PROV_METHOD:
554                result = P2pStatus.INCOMPATIBLE_PROVISIONING_METHOD;
555                break;
556
557            case P2pStatusCode.FAIL_REJECTED_BY_USER:
558                result = P2pStatus.REJECTED_BY_USER;
559                break;
560        }
561        return result;
562    }
563}
564
565