/******************************************************************************* * Code contributed to the webinos project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2012 Samsung Electronics(UK) Ltd ******************************************************************************/ package org.webinos.android.impl.discovery; import java.util.Set; import java.util.ArrayList; import android.util.Log; import android.app.Activity; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; import org.meshpoint.anode.AndroidContext; import org.meshpoint.anode.module.IModule; import org.meshpoint.anode.module.IModuleContext; import org.webinos.api.PendingOperation; import org.webinos.api.discovery.DiscoveryManager; import org.webinos.api.discovery.Filter; import org.webinos.api.discovery.FindCallback; import org.webinos.api.discovery.Options; import org.webinos.api.discovery.Service; import org.webinos.api.discovery.ServiceType; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothClass; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; public class DiscoveryImpl extends DiscoveryManager implements IModule { private Context androidContext; private BluetoothAdapter mBluetoothAdapter; private static final String TAG = "org.webinos.android.impl.DiscoveryImpl"; private static final boolean D = true; /* hard coded array length */ ArrayList<BluetoothDevice> devicesAvailable = new ArrayList<BluetoothDevice>(10); ArrayList<BluetoothDevice> devicesFound = new ArrayList<BluetoothDevice>(10); DiscoveryServiceImpl srv = new DiscoveryServiceImpl(); /***************************** * DiscoveryManager methods *****************************/ @Override public synchronized PendingOperation findServices( ServiceType serviceType, FindCallback findCallback, Options options, Filter filter) { if(D) Log.v(TAG, "DiscoveryImpl: findservices"); if(serviceType == null) { Log.e(TAG, "DiscoveryImpl: Please specify a serviceType"); return null; } //Check on Bluetooth availability mBluetoothAdapter = getDefaultBluetoothAdapter(); if (mBluetoothAdapter == null) { if(D) Log.e(TAG, "Bluetooth is not available"); return null; } else{ if (!mBluetoothAdapter.isEnabled()){ if(D) Log.d(TAG, "Bluetooth is not enabled"); return null; } else { Log.d(TAG, "Found Bluetooth adapter"); //Start FindService DiscoveryRunnable bluetoothFindService = new BluetoothFindService(serviceType, findCallback, options, filter); Thread t = new Thread(bluetoothFindService); t.start(); Log.v(TAG, "findServices - thread started with id "+(int)t.getId()); return new DiscoveryPendingOperation(t, bluetoothFindService); } } } public void advertServices(String serviceType){ //start advertisement } public String getServiceId(String serviceType){ // TODO Auto-generated method stub - this probably is not applicable for BT discovery return null; } public Service createService(){ DiscoveryServiceImpl srv = new DiscoveryServiceImpl(); return srv; } /***************************** * IModule methods *****************************/ @Override public Object startModule(IModuleContext ctx) { if(D) Log.v(TAG, "DiscoveryImpl: startModule"); androidContext = ((AndroidContext)ctx).getAndroidContext(); return this; } @Override public void stopModule() { /* * perform any module shutdown here ... */ Log.v(TAG, "DiscoveryImpl: stopModule"); } /***************************** * Helpers *****************************/ private static BluetoothAdapter getDefaultBluetoothAdapter() { final ArrayList<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(1); if(adapters.isEmpty()) { final boolean[] ok = new boolean[] { false }; Thread t = new Thread() { public void run() { Looper.prepare(); adapters.add(BluetoothAdapter.getDefaultAdapter()); synchronized (ok) { ok[0]=true; ok.notify(); } Looper.loop(); }; }; t.start(); synchronized (ok) { if (ok[0]==false) { try { ok.wait(); } catch (InterruptedException e) {} } } } return adapters.get(0); } class BluetoothFindService implements DiscoveryRunnable { private ServiceType serviceType; private FindCallback findCallback; private Options options; private Filter filter; private DiscoveryReceiver mReceiver; private boolean stopped; private BluetoothFindService(ServiceType srvType, FindCallback findCB, Options opts, Filter fltr) { serviceType = srvType; findCallback = findCB; options = opts; filter = fltr; stopped = false; mReceiver = new DiscoveryReceiver(serviceType, findCallback, this); if(D) Log.v(TAG,"constructed BluetoothFindService"); } public synchronized boolean isStopped() { return stopped; } public synchronized void stop() { stopped = true; } public void run() { // If we're already discovering, stop it if (mBluetoothAdapter.isDiscovering()) mBluetoothAdapter.cancelDiscovery(); // Request discover from BluetoothAdapter mBluetoothAdapter.startDiscovery(); if(stopped) return; IntentFilter ifilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); androidContext.registerReceiver(mReceiver, ifilter); // Register for broadcasts when discovery has finished ifilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); androidContext.registerReceiver(mReceiver, ifilter); } public void discoveryFinished() { System.out.println("discoveryfinished"); if (mBluetoothAdapter != null) { mBluetoothAdapter.cancelDiscovery(); } androidContext.unregisterReceiver(mReceiver); } } class DiscoveryReceiver extends BroadcastReceiver { private ServiceType serviceType; private FindCallback findCallback; BluetoothFindService bluetoothFindService; @Override public void onReceive(Context context, Intent intent) { if(bluetoothFindService.isStopped()) { Log.v(TAG, "DiscoveryReceiver onReceive - stopped"); return; } String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); devicesAvailable.add(device); } // When device discovery is finished, query services else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //unregister receiver bluetoothFindService.discoveryFinished(); switch(getResultCode()) { case Activity.RESULT_OK: Log.d(TAG, "discovery Okay" +getResultCode()); //get the list of bonded devices Set<BluetoothDevice> devicesPaired = mBluetoothAdapter.getBondedDevices(); if (devicesPaired.isEmpty() && devicesAvailable.isEmpty()) Log.e(TAG, "No bluetooth device is available"); else{ if (!devicesAvailable.isEmpty()) { for (BluetoothDevice device : devicesAvailable) { BluetoothClass bluetoothClass = device.getBluetoothClass(); if ((bluetoothClass != null) && (bluetoothClass.hasService(Integer.parseInt(serviceType.api)))) { boolean dup = false; for (BluetoothDevice founddevice : devicesFound) { if(founddevice.equals(device)) { System.out.println("duplicate device: " + device.getName() ); dup = true; } } if(!dup) devicesFound.add(device); } } } if(!devicesPaired.isEmpty()) { for (BluetoothDevice device : devicesPaired) { BluetoothClass bluetoothClass = device.getBluetoothClass(); if ((bluetoothClass != null) && (bluetoothClass.hasService(Integer.parseInt(serviceType.api)))) { //filter out duplicated devices boolean dup = false; for (BluetoothDevice founddevice: devicesFound) { if(founddevice.equals(device)) dup = true; } if(!dup) { devicesFound.add(device); System.out.println("paired device_name: " + device.getName() ); System.out.println("paired device_address:" + device.getAddress()); } } } } } String[] deviceNames; deviceNames = new String[devicesFound.size() + 1]; String[] deviceAddresses; deviceAddresses = new String[devicesFound.size() + 1]; int i = 0; String[] Names = {null, null , null, null, null, null}; srv.deviceNames = Names; srv.deviceAddresses = Names; for (BluetoothDevice device : devicesFound) { deviceNames[i] = device.getName(); srv.deviceNames[i] = device.getName(); deviceAddresses[i] = device.getAddress(); srv.deviceAddresses[i] = device.getAddress(); System.out.println("device: " + i ); System.out.println("Name:" + deviceNames[i]); System.out.println("Address:" + deviceAddresses[i]); i++; } //return devices found System.out.println("findCallback:" + srv); findCallback.onFound(srv); break; default: Log.d(TAG, "discovery failed" +getResultCode()); // findCallback.onError(new DiscoveryError()); break; } } } private DiscoveryReceiver(ServiceType srvType, FindCallback findCB, BluetoothFindService btFnd) { serviceType = srvType; findCallback = findCB; bluetoothFindService = btFnd; } }; abstract interface DiscoveryRunnable extends Runnable { //for supports on PendingOperation public abstract void stop(); public abstract boolean isStopped(); } class DiscoveryPendingOperation extends PendingOperation { private Thread t=null; private DiscoveryRunnable r=null; public DiscoveryPendingOperation(Thread t, DiscoveryRunnable r) { this.t = t; this.r = r; } public void cancel() { Log.d(TAG, "DiscoveryPendingOperation cancel"); if(t!=null) { Log.v(TAG, "DiscoveryPendingOperation cancel - send interrupt..."); //Is this interrupt needed??? - copied from messaging t.interrupt(); if(r!=null) r.stop(); } } } }