xref: /btstack/port/mtk/SPPClient/src/com/bluekitchen/sppclient/MainActivity.java (revision 16ece13520cb8efcf94cb63eab58a3eda87f40a2)
1 package com.bluekitchen.sppclient;
2 
3 import java.io.UnsupportedEncodingException;
4 import java.util.ArrayList;
5 import java.util.List;
6 
7 import android.annotation.SuppressLint;
8 import android.app.Activity;
9 import android.os.Bundle;
10 import android.util.Log;
11 import android.view.Menu;
12 import android.widget.TextView;
13 
14 import com.bluekitchen.btstack.RFCOMMDataPacket;
15 import com.bluekitchen.btstack.BD_ADDR;
16 import com.bluekitchen.btstack.BTstack;
17 import com.bluekitchen.btstack.Packet;
18 import com.bluekitchen.btstack.PacketHandler;
19 import com.bluekitchen.btstack.Util;
20 import com.bluekitchen.btstack.event.BTstackEventState;
21 import com.bluekitchen.btstack.event.HCIEventCommandComplete;
22 import com.bluekitchen.btstack.event.HCIEventDisconnectionComplete;
23 import com.bluekitchen.btstack.event.HCIEventHardwareError;
24 import com.bluekitchen.btstack.event.HCIEventInquiryComplete;
25 import com.bluekitchen.btstack.event.HCIEventInquiryResultWithRssi;
26 import com.bluekitchen.btstack.event.HCIEventRemoteNameRequestComplete;
27 import com.bluekitchen.btstack.event.RFCOMMEventOpenChannelComplete;
28 import com.bluekitchen.btstack.event.SDPQueryComplete;
29 import com.bluekitchen.btstack.event.SDPQueryRFCOMMService;
30 
31 
32 public class MainActivity extends Activity implements PacketHandler {
33 
34 	// minimal Android text UI
35 
36 	private static final String BTSTACK_TAG = "BTstack";
37 
38 	private TextView tv;
39 	private String onScreenMessage = "";
40 
41 	@Override
42 	protected void onCreate(Bundle savedInstanceState) {
43 		super.onCreate(savedInstanceState);
44 		setContentView(R.layout.activity_main);
45 
46 		tv = new TextView(this);
47 		setContentView(tv);
48 
49 		test();
50 	}
51 
52 	void addMessage(final String message){
53 		onScreenMessage = onScreenMessage + "\n" + message;
54 		Log.d(BTSTACK_TAG, message);
55 		runOnUiThread(new Runnable(){
56 			public void run(){
57 				tv.setText(onScreenMessage);
58 			}
59 		});
60 	}
61 
62 	void addTempMessage(final String message){
63 		Log.d(BTSTACK_TAG, message);
64 		runOnUiThread(new Runnable(){
65 			public void run(){
66 				tv.setText(onScreenMessage +"\n" + message);
67 			}
68 		});
69 	}
70 
71 	void clearMessages(){
72 		onScreenMessage = "";
73 		runOnUiThread(new Runnable(){
74 			public void run(){
75 				tv.setText(onScreenMessage);
76 			}
77 		});
78 	}
79 
80 
81 	@Override
82 	public boolean onCreateOptionsMenu(Menu menu) {
83 		// Inflate the menu; this adds items to the action bar if it is present.
84 		getMenuInflater().inflate(R.menu.main, menu);
85 		return true;
86 	}
87 
88 
89 	// helper class to store inquiry results
90 	private static class RemoteDevice{
91 		private enum NAME_STATE {
92 			REMOTE_NAME_REQUEST, REMOTE_NAME_INQUIRED, REMOTE_NAME_FETCHED
93 		};
94 
95 		private BD_ADDR bdAddr;
96 		private int pageScanRepetitionMode;
97 		private int clockOffset;
98 		private String name;
99 		private NAME_STATE state;
100 
101 		public RemoteDevice(BD_ADDR bdAddr, int pageScanRepetitionMode, int clockOffset) {
102 			this.bdAddr = bdAddr;
103 			this.state = NAME_STATE.REMOTE_NAME_REQUEST;
104 		}
105 
106 		public boolean nameRequest() {
107 			return this.state == NAME_STATE.REMOTE_NAME_REQUEST;
108 		}
109 
110 		public void inquireName(BTstack btstack) {
111 			this.state = NAME_STATE.REMOTE_NAME_INQUIRED;
112 			btstack.HCIRemoteNameRequest(bdAddr, pageScanRepetitionMode, 0, clockOffset);
113 		}
114 
115 		public BD_ADDR getBDAddress() {
116 			return this.bdAddr;
117 		}
118 
119 		private String getName() {
120 			return name;
121 		}
122 
123 		private void setName(String name) {
124 			this.state = NAME_STATE.REMOTE_NAME_FETCHED;
125 			this.name = name;
126 		}
127 	}
128 
129 	// constants
130 
131 	private static final int HCI_INQUIRY_LAP = 0x9E8B33;  // 0x9E8B33: General/Unlimited Inquiry Access Code (GIAC)
132 	private static final int INQUIRY_INTERVAL = 5;
133 	private static final int SPP_UUID = 0x1002;
134 
135 	private enum STATE {
136 		w4_btstack_working, w4_write_inquiry_mode, w4_scan_result, w4_remote_name, w4_sdp_query_result, w4_connected, active
137 	};
138 
139 	private static final String REMOTE_DEVICE_NAME_PREFIX = "BTstack SPP";
140 	private static final String RFCOMM_SERVICE_PREFIX = "SPP";
141 
142 	// state
143 
144 	private BTstack btstack;
145 	private STATE state;
146 	private int testHandle;
147 
148 	private int rfcommChannelID = 0;
149 	private int mtu = 0;
150 	private BD_ADDR remoteBDAddr;
151 
152 	List<SDPQueryRFCOMMService> services = new ArrayList<SDPQueryRFCOMMService>(10);
153 	List<RemoteDevice> devices = new ArrayList<RemoteDevice>(10);
154 
155 	private int counter;
156 
157 
158 	private boolean hasMoreRemoteNameRequests() {
159 		for (RemoteDevice device:devices){
160 			if (device.nameRequest()){
161 				return true;
162 			}
163 		}
164 		return false;
165 	}
166 
167 	private void doNextRemoteNameRequest() {
168 		for (RemoteDevice device:devices){
169 			if (device.nameRequest()){
170 				addMessage("Get remote name of " + device.getBDAddress());
171 	            device.inquireName(btstack);
172 	            return;
173 			}
174 		}
175 	}
176 
177 	private void restartInquiry(){
178 		clearMessages();
179 		addMessage("Restart Inquiry");
180 		state = STATE.w4_scan_result;
181 		services.clear();
182 		devices.clear();
183 		counter = 0;
184 		btstack.HCIInquiry(HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0);
185 	}
186 
187 	@SuppressLint("DefaultLocale")
188 	public void handlePacket(Packet packet){
189 		if (packet instanceof HCIEventHardwareError){
190 			clearMessages();
191 			addMessage("Received HCIEventHardwareError, \nhandle power cycle of the Bluetooth \nchip of the device.");
192 		}
193 
194 		if (packet instanceof HCIEventDisconnectionComplete){
195 			HCIEventDisconnectionComplete event = (HCIEventDisconnectionComplete) packet;
196 			testHandle = event.getConnectionHandle();
197 			addMessage(String.format("Received disconnect, status %d, handle %x", event.getStatus(), testHandle));
198 			restartInquiry();
199 			return;
200 		}
201 
202 		switch (state){
203 		case w4_btstack_working:
204 			if (packet instanceof BTstackEventState){
205 				BTstackEventState event = (BTstackEventState) packet;
206 				if (event.getState() == 2)	{
207 					addMessage("BTstack working. Set write inquiry mode.");
208 					state = STATE.w4_write_inquiry_mode;
209 					btstack.HCIWriteInquiryMode(1); // with RSSI
210 				}
211 			}
212 			break;
213 
214 		case w4_write_inquiry_mode:
215 			if (packet instanceof HCIEventCommandComplete){
216 				state = STATE.w4_scan_result;
217 				btstack.HCIInquiry(HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0);
218 			}
219 			break;
220 
221 		case w4_scan_result:
222 			if (packet instanceof HCIEventInquiryResultWithRssi){
223 				HCIEventInquiryResultWithRssi result = (HCIEventInquiryResultWithRssi) packet;
224 				devices.add(new RemoteDevice(result.getBdAddr(), result.getPageScanRepetitionMode(), result.getClockOffset()));
225 				addMessage("Found device: " + result.getBdAddr());
226 			}
227 
228 			if (packet instanceof HCIEventInquiryComplete){
229 				state = STATE.w4_remote_name;
230 				if (hasMoreRemoteNameRequests()){
231 			        doNextRemoteNameRequest();
232 			        break;
233 			    }
234 			}
235 			break;
236 
237 		case w4_remote_name:
238 			if (packet instanceof HCIEventRemoteNameRequestComplete){
239 				HCIEventRemoteNameRequestComplete result = (HCIEventRemoteNameRequestComplete) packet;
240 				if (result.getStatus() == 0){
241 					// store name on success
242 					setNameForDeviceWithBDAddr(result.getRemoteName(), result.getBdAddr());
243 				}
244 
245 				if (hasMoreRemoteNameRequests()){
246 			        doNextRemoteNameRequest();
247 			        break;
248 			    }
249 
250 				// discovery done, connect to device with remote name prefix
251 
252 				RemoteDevice remoteDevice = null;
253 				for (RemoteDevice device : devices){
254 					if (device.getName() != null && device.getName().startsWith(REMOTE_DEVICE_NAME_PREFIX)){
255 						remoteDevice = device;
256 					}
257 				}
258 
259 				// try first one otherwise
260 				if (remoteDevice == null && devices.size() > 0){
261 					remoteDevice = devices.get(0);
262 				}
263 
264 				// no device, restart inquiry
265 				if (remoteDevice == null){
266 					restartInquiry();
267 					break;
268 				}
269 
270 				// start SDP query for RFCOMMM services
271 				remoteBDAddr = remoteDevice.getBDAddress();
272 				if (remoteDevice.getName() == null){
273 					addMessage("Start SDP Query of " + remoteDevice.getBDAddress());
274 				} else {
275 					addMessage("Start SDP Query of " + remoteDevice.getName());
276 				}
277 				state = STATE.w4_sdp_query_result;
278 				byte[] serviceSearchPattern = Util.serviceSearchPatternForUUID16(SPP_UUID);
279 				btstack.SDPClientQueryRFCOMMServices(remoteBDAddr, serviceSearchPattern);
280 				break;
281 			}
282 			break;
283 
284 		case w4_sdp_query_result:
285 			if (packet instanceof SDPQueryRFCOMMService){
286 				SDPQueryRFCOMMService service = (SDPQueryRFCOMMService) packet;
287 				services.add(service);
288 				addMessage(String.format("Found \"%s\", channel %d", service.getName(), service.getRFCOMMChannel()));
289 			}
290 			if (packet instanceof SDPQueryComplete){
291 				// find service with "SPP" prefix
292 				SDPQueryRFCOMMService selectedService = null;
293 				for  (SDPQueryRFCOMMService service : services){
294 					if (service.getName().startsWith(RFCOMM_SERVICE_PREFIX)){
295 						selectedService = service;
296 						break;
297 					}
298 				}
299 				// restart demo, if no service with prefix found
300 				if (selectedService == null){
301 					restartInquiry();
302 					break;
303 				}
304 
305 				// connect
306 				state = STATE.w4_connected;
307 				clearMessages();
308 				addMessage("SPP Test Application / Part 2");
309 				addMessage("Connect to channel nr " + selectedService.getRFCOMMChannel());
310 				btstack.RFCOMMCreateChannel(remoteBDAddr, selectedService.getRFCOMMChannel());
311 			}
312 
313 			break;
314 
315 		case w4_connected:
316 			if (packet instanceof RFCOMMEventOpenChannelComplete){
317 				RFCOMMEventOpenChannelComplete e = (RFCOMMEventOpenChannelComplete) packet;
318 				if (e.getStatus() != 0) {
319 					addMessage("RFCOMM channel open failed, status " + e.getStatus());
320 				} else {
321 					state = STATE.active;
322 					rfcommChannelID = e.getRFCOMMCid();
323 					mtu = e.getMaxFrameSize();
324 					addMessage(String.format("RFCOMM channel open succeeded. \nNew RFCOMM Channel ID %d,\n max frame size %d", rfcommChannelID, mtu));
325 
326 					counter = 0;
327 					new Thread(new Runnable(){
328 						@Override
329 						public void run() {
330 							try {
331 								while(state == STATE.active){
332 									Thread.sleep(1000);
333 									byte[] data;
334 									try {
335 										data = String.format("BTstack SPP Counter %d\n", counter).getBytes("utf8");
336 										if (counter < Integer.MAX_VALUE){
337 											counter++;
338 										} else {
339 											counter = 0;
340 										}
341 										btstack.RFCOMMSendData(rfcommChannelID, data);
342 									} catch (UnsupportedEncodingException e) {
343 										// TODO Auto-generated catch block
344 										e.printStackTrace();
345 									}
346 								}
347 							} catch (InterruptedException e) {}
348 						}
349 					}).start();
350 				}
351 			}
352 			break;
353 
354 		case active:
355 			if (packet instanceof RFCOMMDataPacket){
356 				addTempMessage("Received RFCOMM data packet: \n" + packet.toString());
357 			}
358 			break;
359 
360 		default:
361 			break;
362 		}
363 	}
364 
365 	private RemoteDevice deviceForBDAddr(BD_ADDR bdAddr){
366 		for (RemoteDevice device:devices){
367 			if (device.getBDAddress().equals(bdAddr) )
368 				return device;
369 		}
370 		return null;
371 	}
372 
373 	private void setNameForDeviceWithBDAddr(String remoteName, BD_ADDR bdAddr) {
374 		RemoteDevice device = deviceForBDAddr(bdAddr);
375 		if (device != null){
376 			addMessage("Found " + remoteName);
377 			device.setName(remoteName);
378 		}
379 	}
380 
381 	void test(){
382 		counter = 0;
383 		addMessage("SPP Test Application");
384 
385 		btstack = new BTstack();
386 		btstack.registerPacketHandler(this);
387 
388 		boolean ok = btstack.connect();
389 		if (!ok) {
390 			addMessage("Failed to connect to BTstack Server");
391 			return;
392 		}
393 
394 		addMessage("BTstackSetPowerMode(1)");
395 
396 		state = STATE.w4_btstack_working;
397 		btstack.BTstackSetPowerMode(1);
398 	}
399 }
400 
401