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