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.googlecode.android_scripting.facade.wifi;
18
19import android.app.Service;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.net.NetworkInfo;
25import android.net.wifi.WpsInfo;
26import android.net.wifi.p2p.WifiP2pConfig;
27import android.net.wifi.p2p.WifiP2pDevice;
28import android.net.wifi.p2p.WifiP2pDeviceList;
29import android.net.wifi.p2p.WifiP2pGroup;
30import android.net.wifi.p2p.WifiP2pGroupList;
31import android.net.wifi.p2p.WifiP2pInfo;
32import android.net.wifi.p2p.WifiP2pManager;
33import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
34import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
35import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
36import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo;
37import android.os.Bundle;
38import android.os.Message;
39import android.os.Messenger;
40import android.os.RemoteException;
41
42import com.android.internal.util.Protocol;
43import com.googlecode.android_scripting.Log;
44import com.googlecode.android_scripting.facade.EventFacade;
45import com.googlecode.android_scripting.facade.FacadeManager;
46import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
47import com.googlecode.android_scripting.rpc.Rpc;
48import com.googlecode.android_scripting.rpc.RpcParameter;
49
50import java.net.InetAddress;
51import java.util.ArrayList;
52import java.util.Collection;
53import java.util.HashMap;
54import java.util.List;
55import java.util.Map;
56
57import org.json.JSONException;
58import org.json.JSONObject;
59
60/**
61 * WifiP2pManager functions.
62 */
63public class WifiP2pManagerFacade extends RpcReceiver {
64
65    class WifiP2pActionListener implements WifiP2pManager.ActionListener {
66        private final EventFacade mEventFacade;
67        private final String mEventType;
68        private final String TAG;
69
70        public WifiP2pActionListener(EventFacade eventFacade, String tag) {
71            mEventType = "WifiP2p";
72            mEventFacade = eventFacade;
73            TAG = tag;
74        }
75
76        @Override
77        public void onSuccess() {
78            mEventFacade.postEvent(mEventType + TAG + "OnSuccess", null);
79        }
80
81        @Override
82        public void onFailure(int reason) {
83            Log.d("WifiActionListener  " + mEventType);
84            Bundle msg = new Bundle();
85            if (reason == WifiP2pManager.P2P_UNSUPPORTED) {
86                msg.putString("reason", "P2P_UNSUPPORTED");
87            } else if (reason == WifiP2pManager.ERROR) {
88                msg.putString("reason", "ERROR");
89            } else if (reason == WifiP2pManager.BUSY) {
90                msg.putString("reason", "BUSY");
91            } else if (reason == WifiP2pManager.NO_SERVICE_REQUESTS) {
92                msg.putString("reason", "NO_SERVICE_REQUESTS");
93            } else {
94                msg.putInt("reason", reason);
95            }
96            mEventFacade.postEvent(mEventType + TAG + "OnFailure", msg);
97        }
98    }
99
100    class WifiP2pConnectionInfoListener implements WifiP2pManager.ConnectionInfoListener {
101        private final EventFacade mEventFacade;
102        private final String mEventType;
103
104        public WifiP2pConnectionInfoListener(EventFacade eventFacade) {
105            mEventType = "WifiP2p";
106            mEventFacade = eventFacade;
107        }
108
109        @Override
110        public void onConnectionInfoAvailable(WifiP2pInfo info) {
111            Bundle msg = new Bundle();
112            msg.putBoolean("groupFormed", info.groupFormed);
113            msg.putBoolean("isGroupOwner", info.isGroupOwner);
114            InetAddress addr = info.groupOwnerAddress;
115            String hostName = null;
116            String hostAddress = null;
117            if (addr != null) {
118                hostName = addr.getHostName();
119                hostAddress = addr.getHostAddress();
120            }
121            msg.putString("groupOwnerHostName", hostName);
122            msg.putString("groupOwnerHostAddress", hostAddress);
123            mEventFacade.postEvent(mEventType + "OnConnectionInfoAvailable", msg);
124        }
125    }
126
127    class WifiP2pDnsSdServiceResponseListener implements
128            WifiP2pManager.DnsSdServiceResponseListener {
129        private final EventFacade mEventFacade;
130        private final String mEventType;
131
132        public WifiP2pDnsSdServiceResponseListener(EventFacade eventFacade) {
133            mEventType = "WifiP2p";
134            mEventFacade = eventFacade;
135        }
136
137        @Override
138        public void onDnsSdServiceAvailable(String instanceName, String registrationType,
139                WifiP2pDevice srcDevice) {
140            Bundle msg = new Bundle();
141            msg.putString("InstanceName", instanceName);
142            msg.putString("RegistrationType", registrationType);
143            msg.putString("SourceDeviceName", srcDevice.deviceName);
144            msg.putString("SourceDeviceAddress", srcDevice.deviceAddress);
145            mEventFacade.postEvent(mEventType + "OnDnsSdServiceAvailable", msg);
146        }
147    }
148
149    class WifiP2pDnsSdTxtRecordListener implements WifiP2pManager.DnsSdTxtRecordListener {
150        private final EventFacade mEventFacade;
151        private final String mEventType;
152
153        public WifiP2pDnsSdTxtRecordListener(EventFacade eventFacade) {
154            mEventType = "WifiP2p";
155            mEventFacade = eventFacade;
156        }
157
158        @Override
159        public void onDnsSdTxtRecordAvailable(String fullDomainName,
160                Map<String, String> txtRecordMap, WifiP2pDevice srcDevice) {
161            Bundle msg = new Bundle();
162            msg.putString("FullDomainName", fullDomainName);
163            Bundle txtMap = new Bundle();
164            for (String key : txtRecordMap.keySet()) {
165                txtMap.putString(key, txtRecordMap.get(key));
166            }
167            msg.putBundle("TxtRecordMap", txtMap);
168            msg.putString("SourceDeviceName", srcDevice.deviceName);
169            msg.putString("SourceDeviceAddress", srcDevice.deviceAddress);
170            mEventFacade.postEvent(mEventType + "OnDnsSdTxtRecordAvailable", msg);
171        }
172
173    }
174
175    class WifiP2pGroupInfoListener implements WifiP2pManager.GroupInfoListener {
176        private final EventFacade mEventFacade;
177        private final String mEventType;
178
179        public WifiP2pGroupInfoListener(EventFacade eventFacade) {
180            mEventType = "WifiP2p";
181            mEventFacade = eventFacade;
182        }
183
184        @Override
185        public void onGroupInfoAvailable(WifiP2pGroup group) {
186            mEventFacade.postEvent(mEventType + "OnGroupInfoAvailable", parseGroupInfo(group));
187        }
188    }
189
190    class WifiP2pPeerListListener implements WifiP2pManager.PeerListListener {
191        private final EventFacade mEventFacade;
192
193        public WifiP2pPeerListListener(EventFacade eventFacade) {
194            mEventFacade = eventFacade;
195        }
196
197        @Override
198        public void onPeersAvailable(WifiP2pDeviceList newPeers) {
199            Collection<WifiP2pDevice> devices = newPeers.getDeviceList();
200            Log.d(devices.toString());
201            if (devices.size() > 0) {
202                mP2pPeers.clear();
203                mP2pPeers.addAll(devices);
204                Bundle msg = new Bundle();
205                msg.putParcelableList("Peers", mP2pPeers);
206                mEventFacade.postEvent(mEventType + "OnPeersAvailable", msg);
207            }
208        }
209    }
210
211    class WifiP2pPersistentGroupInfoListener implements WifiP2pManager.PersistentGroupInfoListener {
212        private final EventFacade mEventFacade;
213        private final String mEventType;
214
215        public WifiP2pPersistentGroupInfoListener(EventFacade eventFacade) {
216            mEventType = "WifiP2p";
217            mEventFacade = eventFacade;
218        }
219
220        @Override
221        public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups) {
222            ArrayList<Bundle> gs = new ArrayList<Bundle>();
223            for (WifiP2pGroup g : groups.getGroupList()) {
224                gs.add(parseGroupInfo(g));
225            }
226            mEventFacade.postEvent(mEventType + "OnPersistentGroupInfoAvailable", gs);
227        }
228
229    }
230
231    class WifiP2pStateChangedReceiver extends BroadcastReceiver {
232        private final EventFacade mEventFacade;
233        private final Bundle mResults;
234
235        WifiP2pStateChangedReceiver(EventFacade eventFacade) {
236            mEventFacade = eventFacade;
237            mResults = new Bundle();
238        }
239
240        @Override
241        public void onReceive(Context c, Intent intent) {
242            String action = intent.getAction();
243            if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
244                Log.d("Wifi P2p State Changed.");
245                int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, 0);
246                if (state == WifiP2pManager.WIFI_P2P_STATE_DISABLED) {
247                    Log.d("Disabled");
248                    isP2pEnabled = false;
249                } else if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
250                    Log.d("Enabled");
251                    isP2pEnabled = true;
252                }
253            } else if (action.equals(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)) {
254                Log.d("Wifi P2p Peers Changed. Requesting peers.");
255                WifiP2pDeviceList peers = intent
256                        .getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
257                Log.d(peers.toString());
258                wifiP2pRequestPeers();
259            } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
260                Log.d("Wifi P2p Connection Changed.");
261                WifiP2pInfo p2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
262                NetworkInfo networkInfo = intent
263                        .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
264                WifiP2pGroup group = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
265                if (networkInfo.isConnected()) {
266                    Log.d("Wifi P2p Connected.");
267                    mResults.putParcelable("P2pInfo", p2pInfo);
268                    mResults.putParcelable("Group", group);
269                    mEventFacade.postEvent(mEventType + "Connected", mResults);
270                    mResults.clear();
271                } else {
272                    mEventFacade.postEvent(mEventType + "Disconnected", null);
273                }
274            } else if (action.equals(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)) {
275                Log.d("Wifi P2p This Device Changed.");
276                WifiP2pDevice device = intent
277                        .getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
278                mResults.putParcelable("Device", device);
279                mEventFacade.postEvent(mEventType + "ThisDeviceChanged", mResults);
280                mResults.clear();
281            } else if (action.equals(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION)) {
282                Log.d("Wifi P2p Discovery Changed.");
283                int state = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, 0);
284                if (state == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
285                    Log.d("discovery started.");
286                } else if (state == WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED) {
287                    Log.d("discovery stoped.");
288                }
289            }
290        }
291    }
292
293    private final static String mEventType = "WifiP2p";
294
295    private WifiP2pManager.Channel mChannel;
296    private final EventFacade mEventFacade;
297    private final WifiP2pManager mP2p;
298    private final WifiP2pStateChangedReceiver mP2pStateChangedReceiver;
299    private final Service mService;
300    private final IntentFilter mStateChangeFilter;
301    private final Map<Integer, WifiP2pServiceRequest> mServiceRequests;
302
303    private boolean isP2pEnabled;
304    private int mServiceRequestCnt = 0;
305    private WifiP2pServiceInfo mServiceInfo = null;
306    private List<WifiP2pDevice> mP2pPeers = new ArrayList<WifiP2pDevice>();
307
308    public WifiP2pManagerFacade(FacadeManager manager) {
309        super(manager);
310        mService = manager.getService();
311        mP2p = (WifiP2pManager) mService.getSystemService(Context.WIFI_P2P_SERVICE);
312        mEventFacade = manager.getReceiver(EventFacade.class);
313
314        mStateChangeFilter = new IntentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
315        mStateChangeFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
316        mStateChangeFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
317        mStateChangeFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
318        mStateChangeFilter.setPriority(999);
319
320        mP2pStateChangedReceiver = new WifiP2pStateChangedReceiver(mEventFacade);
321        mServiceRequests = new HashMap<Integer, WifiP2pServiceRequest>();
322    }
323
324    public Bundle parseGroupInfo(WifiP2pGroup group) {
325        Bundle msg = new Bundle();
326        msg.putString("Interface", group.getInterface());
327        msg.putString("NetworkName", group.getNetworkName());
328        msg.putString("Passphrase", group.getPassphrase());
329        msg.putInt("NetworkId", group.getNetworkId());
330        msg.putString("OwnerName", group.getOwner().deviceName);
331        msg.putString("OwnerAddress", group.getOwner().deviceAddress);
332        return msg;
333    }
334
335    @Override
336    public void shutdown() {
337        mService.unregisterReceiver(mP2pStateChangedReceiver);
338    }
339
340    @Rpc(description = "Accept p2p connection invitation.")
341    public void wifiP2pAcceptConnection() throws RemoteException {
342        Log.d("Accepting p2p connection.");
343        Messenger m = mP2p.getP2pStateMachineMessenger();
344        int user_accept = Protocol.BASE_WIFI_P2P_SERVICE + 2;
345        Message msg = Message.obtain();
346        msg.what = user_accept;
347        m.send(msg);
348    }
349
350    @Rpc(description = "Reject p2p connection invitation.")
351    public void wifiP2pRejectConnection() throws RemoteException {
352        Log.d("Rejecting p2p connection.");
353        Messenger m = mP2p.getP2pStateMachineMessenger();
354        int user_accept = Protocol.BASE_WIFI_P2P_SERVICE + 3;
355        Message msg = Message.obtain();
356        msg.what = user_accept;
357        m.send(msg);
358    }
359
360    @Rpc(description = "Register a local service for service discovery. One of the \"CreateXxxServiceInfo functions needs to be called first.\"")
361    public void wifiP2pAddLocalService() {
362        mP2p.addLocalService(mChannel, mServiceInfo,
363                new WifiP2pActionListener(mEventFacade, "AddLocalService"));
364    }
365
366    @Rpc(description = "Add a service discovery request.")
367    public Integer wifiP2pAddServiceRequest(
368            @RpcParameter(name = "protocolType") Integer protocolType) {
369        WifiP2pServiceRequest request = WifiP2pServiceRequest.newInstance(protocolType);
370        mServiceRequestCnt += 1;
371        mServiceRequests.put(mServiceRequestCnt, request);
372        mP2p.addServiceRequest(mChannel, request, new WifiP2pActionListener(mEventFacade,
373                "AddServiceRequest"));
374        return mServiceRequestCnt;
375    }
376
377    @Rpc(description = "Cancel any ongoing connect negotiation.")
378    public void wifiP2pCancelConnect() {
379        mP2p.cancelConnect(mChannel, new WifiP2pActionListener(mEventFacade, "CancelConnect"));
380    }
381
382    @Rpc(description = "Clear all registered local services of service discovery.")
383    public void wifiP2pClearLocalServices() {
384        mP2p.clearLocalServices(mChannel,
385                new WifiP2pActionListener(mEventFacade, "ClearLocalServices"));
386    }
387
388    @Rpc(description = "Clear all registered service discovery requests.")
389    public void wifiP2pClearServiceRequests() {
390        mP2p.clearServiceRequests(mChannel,
391                new WifiP2pActionListener(mEventFacade, "ClearServiceRequests"));
392    }
393
394    @Rpc(description = "Connects to a discovered wifi p2p device.")
395    public void wifiP2pConnect(@RpcParameter(name = "deviceId") String deviceId) {
396        for (WifiP2pDevice d : mP2pPeers) {
397            if (wifiP2pDeviceMatches(d, deviceId)) {
398                WifiP2pConfig config = new WifiP2pConfig();
399                config.deviceAddress = d.deviceAddress;
400                config.wps.setup = WpsInfo.PBC;
401                mP2p.connect(mChannel, config,
402                        new WifiP2pActionListener(mEventFacade, "Connect"));
403            }
404        }
405    }
406
407    @Rpc(description = "Create a Bonjour service info object to be used for wifiP2pAddLocalService.")
408    public void wifiP2pCreateBonjourServiceInfo(
409            @RpcParameter(name = "instanceName") String instanceName,
410            @RpcParameter(name = "serviceType") String serviceType,
411            @RpcParameter(name = "txtMap") JSONObject txtMap) throws JSONException {
412        Map<String, String> map = new HashMap<String, String>();
413        for (String key : txtMap.keySet()) {
414            map.put(key, txtMap.getString(key));
415        }
416        mServiceInfo = WifiP2pDnsSdServiceInfo.newInstance(instanceName, serviceType, map);
417    }
418
419    @Rpc(description = "Create a wifi p2p group.")
420    public void wifiP2pCreateGroup() {
421        mP2p.createGroup(mChannel, new WifiP2pActionListener(mEventFacade, "CreatGroup"));
422    }
423
424    @Rpc(description = "Create a Upnp service info object to be used for wifiP2pAddLocalService.")
425    public void wifiP2pCreateUpnpServiceInfo(
426            @RpcParameter(name = "uuid") String uuid,
427            @RpcParameter(name = "device") String device,
428            @RpcParameter(name = "services") List<String> services) {
429        mServiceInfo = WifiP2pUpnpServiceInfo.newInstance(uuid, device, services);
430    }
431
432    @Rpc(description = "Delete a stored persistent group from the system settings.")
433    public void wifiP2pDeletePersistentGroup(@RpcParameter(name = "netId") Integer netId) {
434        mP2p.deletePersistentGroup(mChannel, netId,
435                new WifiP2pActionListener(mEventFacade, "DeletePersistentGroup"));
436    }
437
438    private boolean wifiP2pDeviceMatches(WifiP2pDevice d, String deviceId) {
439        return d.deviceName.equals(deviceId) || d.deviceAddress.equals(deviceId);
440    }
441
442    @Rpc(description = "Start peers discovery for wifi p2p.")
443    public void wifiP2pDiscoverPeers() {
444        mP2p.discoverPeers(mChannel, new WifiP2pActionListener(mEventFacade, "DiscoverPeers"));
445    }
446
447    @Rpc(description = "Initiate service discovery.")
448    public void wifiP2pDiscoverServices() {
449        mP2p.discoverServices(mChannel,
450                new WifiP2pActionListener(mEventFacade, "DiscoverServices"));
451    }
452
453    @Rpc(description = "Initialize wifi p2p. Must be called before any other p2p functions.")
454    public void wifiP2pInitialize() {
455        mService.registerReceiver(mP2pStateChangedReceiver, mStateChangeFilter);
456        mChannel = mP2p.initialize(mService, mService.getMainLooper(), null);
457    }
458
459    @Rpc(description = "Close the current wifi p2p connection created with initialize.")
460    public void wifiP2pClose() {
461        if (mChannel != null) {
462            mChannel.close();
463        }
464    }
465
466    @Rpc(description = "Returns true if wifi p2p is enabled, false otherwise.")
467    public Boolean wifiP2pIsEnabled() {
468        return isP2pEnabled;
469    }
470
471    @Rpc(description = "Remove the current p2p group.")
472    public void wifiP2pRemoveGroup() {
473        mP2p.removeGroup(mChannel, new WifiP2pActionListener(mEventFacade, "RemoveGroup"));
474    }
475
476    @Rpc(description = "Remove a registered local service added with wifiP2pAddLocalService.")
477    public void wifiP2pRemoveLocalService() {
478        mP2p.removeLocalService(mChannel, mServiceInfo,
479                new WifiP2pActionListener(mEventFacade, "RemoveLocalService"));
480    }
481
482    @Rpc(description = "Remove a service discovery request.")
483    public void wifiP2pRemoveServiceRequest(@RpcParameter(name = "index") Integer index) {
484        mP2p.removeServiceRequest(mChannel, mServiceRequests.remove(index),
485                new WifiP2pActionListener(mEventFacade, "RemoveServiceRequest"));
486    }
487
488    @Rpc(description = "Request device connection info.")
489    public void wifiP2pRequestConnectionInfo() {
490        mP2p.requestConnectionInfo(mChannel, new WifiP2pConnectionInfoListener(mEventFacade));
491    }
492
493    @Rpc(description = "Create a wifi p2p group.")
494    public void wifiP2pRequestGroupInfo() {
495        mP2p.requestGroupInfo(mChannel, new WifiP2pGroupInfoListener(mEventFacade));
496    }
497
498    @Rpc(description = "Request peers that are discovered for wifi p2p.")
499    public void wifiP2pRequestPeers() {
500        mP2p.requestPeers(mChannel, new WifiP2pPeerListListener(mEventFacade));
501    }
502
503    @Rpc(description = "Request a list of all the persistent p2p groups stored in system.")
504    public void wifiP2pRequestPersistentGroupInfo() {
505        mP2p.requestPersistentGroupInfo(mChannel,
506                new WifiP2pPersistentGroupInfoListener(mEventFacade));
507    }
508
509    @Rpc(description = "Set p2p device name.")
510    public void wifiP2pSetDeviceName(@RpcParameter(name = "devName") String devName) {
511        mP2p.setDeviceName(mChannel, devName,
512                new WifiP2pActionListener(mEventFacade, "SetDeviceName"));
513    }
514
515    @Rpc(description = "Register a callback to be invoked on receiving Bonjour service discovery response.")
516    public void wifiP2pSetDnsSdResponseListeners() {
517        mP2p.setDnsSdResponseListeners(mChannel,
518                new WifiP2pDnsSdServiceResponseListener(mEventFacade),
519                new WifiP2pDnsSdTxtRecordListener(mEventFacade));
520    }
521
522    @Rpc(description = "Stop an ongoing peer discovery.")
523    public void wifiP2pStopPeerDiscovery() {
524        mP2p.stopPeerDiscovery(mChannel,
525                new WifiP2pActionListener(mEventFacade, "StopPeerDiscovery"));
526    }
527
528}
529