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