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.googlecode.android_scripting.facade.wifi; 18 19 import android.app.Service; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.net.NetworkInfo; 25 import android.net.wifi.WpsInfo; 26 import android.net.wifi.p2p.WifiP2pConfig; 27 import android.net.wifi.p2p.WifiP2pDevice; 28 import android.net.wifi.p2p.WifiP2pDeviceList; 29 import android.net.wifi.p2p.WifiP2pGroup; 30 import android.net.wifi.p2p.WifiP2pGroupList; 31 import android.net.wifi.p2p.WifiP2pInfo; 32 import android.net.wifi.p2p.WifiP2pManager; 33 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo; 34 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; 35 import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; 36 import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo; 37 import android.os.Bundle; 38 import android.os.Message; 39 import android.os.Messenger; 40 import android.os.RemoteException; 41 42 import com.android.internal.util.Protocol; 43 import com.googlecode.android_scripting.Log; 44 import com.googlecode.android_scripting.facade.EventFacade; 45 import com.googlecode.android_scripting.facade.FacadeManager; 46 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 47 import com.googlecode.android_scripting.rpc.Rpc; 48 import com.googlecode.android_scripting.rpc.RpcParameter; 49 50 import java.net.InetAddress; 51 import java.util.ArrayList; 52 import java.util.Collection; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 57 import org.json.JSONException; 58 import org.json.JSONObject; 59 60 /** 61 * WifiP2pManager functions. 62 */ 63 public 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