1 2 package com.example.android.wifidirect.discovery; 3 4 import android.Manifest; 5 import android.app.Activity; 6 import android.app.Fragment; 7 import android.content.BroadcastReceiver; 8 import android.content.Context; 9 import android.content.IntentFilter; 10 import android.content.pm.PackageManager; 11 import android.net.wifi.WpsInfo; 12 import android.net.wifi.p2p.WifiP2pConfig; 13 import android.net.wifi.p2p.WifiP2pDevice; 14 import android.net.wifi.p2p.WifiP2pInfo; 15 import android.net.wifi.p2p.WifiP2pManager; 16 import android.net.wifi.p2p.WifiP2pManager.ActionListener; 17 import android.net.wifi.p2p.WifiP2pManager.Channel; 18 import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener; 19 import android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener; 20 import android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener; 21 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo; 22 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest; 23 import android.os.Build; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.util.Log; 28 import android.view.View; 29 import android.widget.TextView; 30 31 import com.example.android.wifidirect.discovery.WiFiChatFragment.MessageTarget; 32 import com.example.android.wifidirect.discovery.WiFiDirectServicesList.DeviceClickListener; 33 import com.example.android.wifidirect.discovery.WiFiDirectServicesList.WiFiDevicesAdapter; 34 35 import java.io.IOException; 36 import java.util.HashMap; 37 import java.util.Map; 38 39 /** 40 * The main activity for the sample. This activity registers a local service and 41 * perform discovery over Wi-Fi p2p network. It also hosts a couple of fragments 42 * to manage chat operations. When the app is launched, the device publishes a 43 * chat service and also tries to discover services published by other peers. On 44 * selecting a peer published service, the app initiates a Wi-Fi P2P (Direct) 45 * connection with the peer. On successful connection with a peer advertising 46 * the same service, the app opens up sockets to initiate a chat. 47 * {@code WiFiChatFragment} is then added to the the main activity which manages 48 * the interface and messaging needs for a chat session. 49 */ 50 public class WiFiServiceDiscoveryActivity extends Activity implements 51 DeviceClickListener, Handler.Callback, MessageTarget, 52 ConnectionInfoListener { 53 54 public static final String TAG = "wifidirectdemo"; 55 56 // TXT RECORD properties 57 public static final String TXTRECORD_PROP_AVAILABLE = "available"; 58 public static final String SERVICE_INSTANCE = "_wifidemotest"; 59 public static final String SERVICE_REG_TYPE = "_presence._tcp"; 60 61 public static final int MESSAGE_READ = 0x400 + 1; 62 public static final int MY_HANDLE = 0x400 + 2; 63 64 private static final int PERMISSIONS_REQUEST_CODE = 1001; 65 66 private WifiP2pManager manager; 67 68 static final int SERVER_PORT = 4545; 69 70 private final IntentFilter intentFilter = new IntentFilter(); 71 private Channel channel; 72 private BroadcastReceiver receiver = null; 73 private WifiP2pDnsSdServiceRequest serviceRequest; 74 75 private Handler handler = new Handler(this); 76 private WiFiChatFragment chatFragment; 77 private WiFiDirectServicesList servicesList; 78 79 private TextView statusTxtView; 80 getHandler()81 public Handler getHandler() { 82 return handler; 83 } 84 setHandler(Handler handler)85 public void setHandler(Handler handler) { 86 this.handler = handler; 87 } 88 89 @Override onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)90 public void onRequestPermissionsResult(int requestCode, String[] permissions, 91 int[] grantResults) { 92 switch (requestCode) { 93 case PERMISSIONS_REQUEST_CODE: 94 if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { 95 Log.e(TAG, "Fine location permission is not granted!"); 96 finish(); 97 } else { 98 startRegistrationAndDiscovery(); 99 } 100 break; 101 } 102 } 103 104 /** Called when the activity is first created. */ 105 @Override onCreate(Bundle savedInstanceState)106 public void onCreate(Bundle savedInstanceState) { 107 super.onCreate(savedInstanceState); 108 setContentView(R.layout.main); 109 statusTxtView = (TextView) findViewById(R.id.status_text); 110 111 intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); 112 intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); 113 intentFilter 114 .addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); 115 intentFilter 116 .addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); 117 118 manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); 119 channel = manager.initialize(this, getMainLooper(), null); 120 121 servicesList = new WiFiDirectServicesList(); 122 getFragmentManager().beginTransaction() 123 .add(R.id.container_root, servicesList, "services").commit(); 124 125 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M 126 && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) 127 != PackageManager.PERMISSION_GRANTED) { 128 requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 129 PERMISSIONS_REQUEST_CODE); 130 // After this point you wait for callback in 131 // onRequestPermissionsResult(int, String[], int[]) overridden method 132 } else { 133 startRegistrationAndDiscovery(); 134 } 135 136 } 137 138 @Override onRestart()139 protected void onRestart() { 140 Fragment frag = getFragmentManager().findFragmentByTag("services"); 141 if (frag != null) { 142 getFragmentManager().beginTransaction().remove(frag).commit(); 143 } 144 super.onRestart(); 145 } 146 147 @Override onStop()148 protected void onStop() { 149 if (manager != null && channel != null) { 150 manager.removeGroup(channel, new ActionListener() { 151 152 @Override 153 public void onFailure(int reasonCode) { 154 Log.d(TAG, "Disconnect failed. Reason :" + reasonCode); 155 } 156 157 @Override 158 public void onSuccess() { 159 } 160 161 }); 162 } 163 super.onStop(); 164 } 165 166 /** 167 * Registers a local service and then initiates a service discovery 168 */ startRegistrationAndDiscovery()169 private void startRegistrationAndDiscovery() { 170 Map<String, String> record = new HashMap<String, String>(); 171 record.put(TXTRECORD_PROP_AVAILABLE, "visible"); 172 173 WifiP2pDnsSdServiceInfo service = WifiP2pDnsSdServiceInfo.newInstance( 174 SERVICE_INSTANCE, SERVICE_REG_TYPE, record); 175 manager.addLocalService(channel, service, new ActionListener() { 176 177 @Override 178 public void onSuccess() { 179 appendStatus("Added Local Service"); 180 } 181 182 @Override 183 public void onFailure(int error) { 184 appendStatus("Failed to add a service"); 185 } 186 }); 187 188 discoverService(); 189 190 } 191 discoverService()192 private void discoverService() { 193 194 /* 195 * Register listeners for DNS-SD services. These are callbacks invoked 196 * by the system when a service is actually discovered. 197 */ 198 199 manager.setDnsSdResponseListeners(channel, 200 new DnsSdServiceResponseListener() { 201 202 @Override 203 public void onDnsSdServiceAvailable(String instanceName, 204 String registrationType, WifiP2pDevice srcDevice) { 205 206 // A service has been discovered. Is this our app? 207 208 if (instanceName.equalsIgnoreCase(SERVICE_INSTANCE)) { 209 210 // update the UI and add the item the discovered 211 // device. 212 WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager() 213 .findFragmentByTag("services"); 214 if (fragment != null) { 215 WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment 216 .getListAdapter()); 217 WiFiP2pService service = new WiFiP2pService(); 218 service.device = srcDevice; 219 service.instanceName = instanceName; 220 service.serviceRegistrationType = registrationType; 221 adapter.add(service); 222 adapter.notifyDataSetChanged(); 223 Log.d(TAG, "onBonjourServiceAvailable " 224 + instanceName); 225 } 226 } 227 228 } 229 }, new DnsSdTxtRecordListener() { 230 231 /** 232 * A new TXT record is available. Pick up the advertised 233 * buddy name. 234 */ 235 @Override 236 public void onDnsSdTxtRecordAvailable( 237 String fullDomainName, Map<String, String> record, 238 WifiP2pDevice device) { 239 Log.d(TAG, 240 device.deviceName + " is " 241 + record.get(TXTRECORD_PROP_AVAILABLE)); 242 } 243 }); 244 245 // After attaching listeners, create a service request and initiate 246 // discovery. 247 serviceRequest = WifiP2pDnsSdServiceRequest.newInstance(); 248 manager.addServiceRequest(channel, serviceRequest, 249 new ActionListener() { 250 251 @Override 252 public void onSuccess() { 253 appendStatus("Added service discovery request"); 254 } 255 256 @Override 257 public void onFailure(int arg0) { 258 appendStatus("Failed adding service discovery request"); 259 } 260 }); 261 manager.discoverServices(channel, new ActionListener() { 262 263 @Override 264 public void onSuccess() { 265 appendStatus("Service discovery initiated"); 266 } 267 268 @Override 269 public void onFailure(int arg0) { 270 appendStatus("Service discovery failed"); 271 272 } 273 }); 274 } 275 276 @Override connectP2p(WiFiP2pService service)277 public void connectP2p(WiFiP2pService service) { 278 WifiP2pConfig config = new WifiP2pConfig(); 279 config.deviceAddress = service.device.deviceAddress; 280 config.wps.setup = WpsInfo.PBC; 281 if (serviceRequest != null) 282 manager.removeServiceRequest(channel, serviceRequest, 283 new ActionListener() { 284 285 @Override 286 public void onSuccess() { 287 } 288 289 @Override 290 public void onFailure(int arg0) { 291 } 292 }); 293 294 manager.connect(channel, config, new ActionListener() { 295 296 @Override 297 public void onSuccess() { 298 appendStatus("Connecting to service"); 299 } 300 301 @Override 302 public void onFailure(int errorCode) { 303 appendStatus("Failed connecting to service"); 304 } 305 }); 306 } 307 308 @Override handleMessage(Message msg)309 public boolean handleMessage(Message msg) { 310 switch (msg.what) { 311 case MESSAGE_READ: 312 byte[] readBuf = (byte[]) msg.obj; 313 // construct a string from the valid bytes in the buffer 314 String readMessage = new String(readBuf, 0, msg.arg1); 315 Log.d(TAG, readMessage); 316 (chatFragment).pushMessage("Buddy: " + readMessage); 317 break; 318 319 case MY_HANDLE: 320 Object obj = msg.obj; 321 (chatFragment).setChatManager((ChatManager) obj); 322 323 } 324 return true; 325 } 326 327 @Override onResume()328 public void onResume() { 329 super.onResume(); 330 receiver = new WiFiDirectBroadcastReceiver(manager, channel, this); 331 registerReceiver(receiver, intentFilter); 332 } 333 334 @Override onPause()335 public void onPause() { 336 super.onPause(); 337 unregisterReceiver(receiver); 338 } 339 340 @Override onConnectionInfoAvailable(WifiP2pInfo p2pInfo)341 public void onConnectionInfoAvailable(WifiP2pInfo p2pInfo) { 342 Thread handler = null; 343 /* 344 * The group owner accepts connections using a server socket and then spawns a 345 * client socket for every client. This is handled by {@code 346 * GroupOwnerSocketHandler} 347 */ 348 349 if (p2pInfo.isGroupOwner) { 350 Log.d(TAG, "Connected as group owner"); 351 try { 352 handler = new GroupOwnerSocketHandler( 353 ((MessageTarget) this).getHandler()); 354 handler.start(); 355 } catch (IOException e) { 356 Log.d(TAG, 357 "Failed to create a server thread - " + e.getMessage()); 358 return; 359 } 360 } else { 361 Log.d(TAG, "Connected as peer"); 362 handler = new ClientSocketHandler( 363 ((MessageTarget) this).getHandler(), 364 p2pInfo.groupOwnerAddress); 365 handler.start(); 366 } 367 chatFragment = new WiFiChatFragment(); 368 getFragmentManager().beginTransaction() 369 .replace(R.id.container_root, chatFragment).commit(); 370 statusTxtView.setVisibility(View.GONE); 371 } 372 appendStatus(String status)373 public void appendStatus(String status) { 374 String current = statusTxtView.getText().toString(); 375 statusTxtView.setText(current + "\n" + status); 376 } 377 } 378