Java tutorial
/* * Copyright (C) 2013 The Android Open Source 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. */ package iha_au.ppmonitor.Services; import android.app.AlertDialog; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.StringTokenizer; import java.util.UUID; import iha_au.ppmonitor.Models.ModelInterfaces.ISensor; import iha_au.ppmonitor.Models.ModelInterfaces.ISensorData; import iha_au.ppmonitor.Models.PressureSensor; import iha_au.ppmonitor.Models.PressureSensorData; /** * Service til at manage forbindelse og data kommunikation med en GATT servier hosted p et givent BLE apparat. */ public class BleService extends Service implements IDataService { public final static String SCAN_RESULT = "Device"; public final static String VALUE_RESULT = "Value"; public final static String BLE_ENABLED = "enable"; private BluetoothGatt bGatt; private BluetoothManager bluetoothManager; private BluetoothAdapter mBluetoothAdapter; boolean hasBle; private BluetoothLeScanner bluetoothLeScanner; private final static String SERVICE_UUID_CHECK = "0000110a-0000-1000-8000-00805f9b34fb"; private final static String SERVICE_UUID_TEST = "0000ffe0-0000-1000-8000-00805f9b34fb"; private final static String DEVICE_ADRESS = "74:DA:EA:B2:1B:1A"; Method getUuidsMethod; public boolean isConnected; BluetoothGattCharacteristic characteristic; ISensorData sensors; private final IBinder binder = new BleBinder(); private boolean isRecording; /** * Nr BleService bliver startet kaldes onCreate. Det er her alle attributter bliver instantieret. */ @Override public void onCreate() { // BluetoothManager bruges til at manage og tilg BlueToothAdapter. bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); // BlueToothAdapter reprsentere enhedens lokale bluetoothadapter. Det er denne klasse, som bruges til at kalde Bluetooth tasks. mBluetoothAdapter = bluetoothManager.getAdapter(); // Gennem denne klasse kan der scannes for Low Energy Bluetooth apparater. bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); hasBle = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); //Metode til afkodning af UUIDS, da disse en collection af bytes. afkodes de til string, for nemmere sammenligning. try { getUuidsMethod = mBluetoothAdapter.getClass().getDeclaredMethod("getUuids", null); } catch (NoSuchMethodException e) { e.printStackTrace(); } Log.v("DEBUG", "bleservce is started"); super.onCreate(); } /** * onStartCommand kaldes efter onCreate igen af android selv. * I denne metode checkes der for at bluetooth er slet til p enhenden. * samt oprettes de sensor objekter som skal bruges til at opvare de vrdier, der modtages fra HW. */ @Override public int onStartCommand(Intent intent, int flags, int startId) { createSensorModels(); if (checkBlueToothEnabled()) { // Hvis Bluetooth er slet til startes scanning for Low Energi apparater startScanForDevice(); } return START_STICKY; } /** * opretter 5 pressureSensor objekter, et til hver sensor i HW. */ private void createSensorModels() { sensors = new PressureSensorData(); for (int i = 0; i < 5; i++) { ISensor s = new PressureSensor("Sensor" + i, new ArrayList<Integer>()); sensors.addSensor(s); } } /** * Opretter forbindelse til HW-bluetooth modulets GATT server. * @param device Det fundne device fra LeScanCallBack. */ private void connecToGatt(BluetoothDevice device) { Log.v("DEBUG", "Get device name: " + device.getName()); bGatt = device.connectGatt(getApplicationContext(), false, gattCallBack); } /** * BluetoothGattCallBack objektet er det som hndtere hvilken reaktion skal ske nr der bliver registreret et * BLE apparat. */ private BluetoothGattCallback gattCallBack = new BluetoothGattCallback() { /** * Nr forbindelsen til det fundne apparat er etableret. * hvis forbindelsen er "Connected" kaldes discoverServices(). * samtidig kaldes broadCastConnection(...), for at fortlle BleConnectionView at der er oprettet forbindelse. * @param gatt det givne apparats GATT server * @param status ? * @param newState forbindelses status: forbundet eller ikke forbundet */ @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { // hvis state er 1 ( er forbundet). if (newState == BluetoothProfile.STATE_CONNECTED) { isConnected = true; try { stopScanForDevice(); broadCastConnection(newState); gatt.discoverServices(); } catch (Exception e) { Log.v("DEBUG", e.toString()); } } super.onConnectionStateChange(gatt, status, newState); } /** * Nr der er oprettet forbindelse og der fundet services p den givne GATT server, hndteres hvilken reaktion der skal ske her. * Der oprettes forbindelse til en specific service, som HW-modulet indeholer. * Herefter hentes de characteristics fra den givne service, og notification sttes til p de givne characteristics. * @param gatt givne apparats GATT server * @param status? */ @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == gatt.GATT_SUCCESS) { Log.v("DEBUG", "I Connected to GATT server with UUID:" + SERVICE_UUID_CHECK); BluetoothGattService service = bGatt .getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")); characteristic = service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb")); Log.v("DEBUG", "cstics::" + characteristic.getUuid().toString()); Log.v("DEBUG", "Icstics values" + characteristic.getValue().length); super.onServicesDiscovered(gatt, status); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } /** * Hver gang characteristics vrdi(er) ndres kaldes onCharacteristicsChanged. * Det er her vrdier fra HW modtages og sendes videre via broadCastValue(..). * @param gatt Det givne apparats GATT server * @param characteristic De Characteristics som er notifikation er slet til p. */ @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { try { long time = System.nanoTime(); String stringValue = characteristic.getStringValue(0); Log.v("DEBUG", stringValue + " Time: " + time); broadCastValue(stringValue); } catch (Exception e) { // Log.v("DEBUG", e.toString()); } super.onCharacteristicChanged(gatt, characteristic); } }; /** * Nr Servicen "slukkes" kaldes onDestroy. * Her disconnectes der fra det tilsluttede aparat. */ @Override public void onDestroy() { Log.v("DEBUG", "onDestroy BleService"); disconnectFromDevice(); super.onDestroy(); } /** * Startet en Intent med filtret SCAN_RESULT. * og broadcaster til systemet. * med int svarende til connection state. * @param connected */ public void broadCastConnection(int connected) { Intent bI = new Intent(SCAN_RESULT); bI.putExtra("connected", connected); LocalBroadcastManager.getInstance(this).sendBroadcast(bI); } /** * Broadcaster om bluetooth er slet til p enheden. * dette sker ved en intent med filtret BLE_ENABLED og en string msg. * @param msg */ public void broadCastBleEnabled(String msg) { Intent broadcastIntent = new Intent(BLE_ENABLED); broadcastIntent.putExtra("message", msg); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent); } /** * broadcaster modtaget string fra onCharacteristicChanged(...), som et PressureSensorData objekt. * @param value */ public void broadCastValue(String value) { try { // StringTokenizer afkoder en string til "tokenz" ( mindre del elementer). Den sepere via en dilimeter ":". // Hver token udgr en vrdi til en sensor. // og de skal derfor sttes ind i deres, matchende sensor. token 1's vrdi, gemmes i sensor 1. StringTokenizer tokens = new StringTokenizer(value, ":"); int count = tokens.countTokens(); for (int i = 0; i < count; i++) { String s = tokens.nextToken(); int v = Integer.parseInt(s); sensors.getSensor(i).addValue(v); } // Intent med filter VALUE_RESULT. indeholdende PressureSensorData objekt, med de sensorer som er blevet opdateret. Intent bI = new Intent(VALUE_RESULT); bI.putExtra("sensors", (Serializable) sensors); LocalBroadcastManager.getInstance(this).sendBroadcast(bI); } catch (Exception e) { Log.v("DEBUG", "error in broadcast value " + e.toString()); } } /** * kaldes af framework nr en activity eller klasse binder sig til service. * @param intent bruges ikke. * @return IBinder objekt af BleBinder. */ @Nullable @Override public IBinder onBind(Intent intent) { return binder; } /** * checker om Bluetooth adapteren er sat til --> hvilket betyder at bluetooth er slet til p enheden. * @return true hvis adapter er enabled. */ @Override public boolean checkBlueToothEnabled() { if (!mBluetoothAdapter.isEnabled()) { // Bluetooth is not enable :) broadCastBleEnabled("Bluetooth is not enabled"); return false; } return true; } /** * starter scanning efter BLE apparater inden for given radius af enheden. * grundet forskellige fremgangsmder ved forskellige SDK versioner, checkes hvilken version * den givne enhed krer, og der handles der efter. */ @Override public void startScanForDevice() { if (hasBle) { // hvis SDK versionen er mindre end 21, alt krer et styresystem ldre end Lollipop if (Build.VERSION.SDK_INT < 21) { mBluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // vil ikke blive implementeret da, det ikke er relevant for denne prototype. // Dette viser kun at gruppen er opmrksom p version kontrol. } }); Log.v("DEBUG", "starting le scan"); } // hvis SDK versionen er 21 eller der over. else { try { // scanning startes igennem LeScanner, som kun sger efter BLE enheder. bluetoothLeScanner.startScan(sCallBack); Log.v("DEBUG", "starting scan"); } catch (Exception e) { Log.v("DEBUG", "ble service destroy"); this.onDestroy(); } } } } /** * Stopper scanning for BLE enheder. * For at sikre fuld stop, stoppes bde LeScanner og Adapter. */ @Override public void stopScanForDevice() { try { bluetoothLeScanner.stopScan(sCallBack); mBluetoothAdapter.getBluetoothLeScanner().stopScan(sCallBack); Log.v("DEBUG", "stopping LEscan"); } catch (Exception e) { Log.v("DEBUG", e.toString()); } } /** * De characteristics fundet i onServiceDiscovered(...) sttes til at notificere BleService * hvergang dets vrdier ndres. Disse vil blive modtaget i onCharacteristicsChanged(...). */ @Override public void startDataRecording() { isRecording = true; try { // Hvis GATT serverens service indeholder characteristics. if (characteristic != null) { // stter GATT serveren til at notificere BleService, nr GATT serverens characteristic's vrdi ndres. bGatt.setCharacteristicNotification(characteristic, true); Log.v("DEBUG", "setting notifikation to true"); } else { Log.v("DEBUG", "No characteristics!!!"); } } catch (Exception e) { Log.v("DEBUG", e.toString() + "no characteristics found"); } } /** * Stter GATT server til ikke at notificere BleService, hver Characteristics vrdi ndres. */ @Override public void stopDataRecording() { isRecording = false; Log.v("DEBUG", "isRecording = " + isRecording); bGatt.setCharacteristicNotification(characteristic, false); Log.v("DEBUG", "setting notifikation to false"); } /** * hvis GATT serveren ikke er null, alts der er oprettet forbindelse til en GATT server. * lukkes denne og forbindelsen afbrydes. * samtidig slettes alle data i det oprettede pressureSensorData objekt. */ @Override public void disconnectFromDevice() { if (bGatt != null) { // lukker gatt server bGatt.close(); // disconnecter fra device bGatt.disconnect(); // sletter data i sensor modeller. sensors.getPressureSensors().clear(); Log.v("DEBUG", "closing connection and dissconnecting and clearing"); } } /** * binder klasse til BleService. bruges nr Activities bindes til BleService. * den indeholder en metode som returnere objekt af BleService. */ public class BleBinder extends Binder { public BleService getBleService() { return BleService.this; } } /** * sCallBack bruges nr der scannes for BLE enehder i startScan(). * ScanCallBack er en android framework abstrakt klasse, der indeholder metoder til behandling af fundne devices. * */ private ScanCallback sCallBack = new ScanCallback() { @Override public void onScanFailed(int errorCode) { Log.v("DEBUG", "ERROR CODE: " + errorCode); super.onScanFailed(errorCode); } /** * Nr et apparat registreres inden for enheden rkkevidde, kaldes onScanResult. * @param callbackType ? * @param result Indeholder det funde apparat. */ @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); try { Log.v("DEBUG", "FOUND DEVICE NAMED: " + result.getDevice().getName()); // hvis det fundne apparats adresse matcher DEVICE_ANDRESS if (result.getDevice().getAddress().equals(DEVICE_ADRESS)) { // hvis det fundne apparats Service's UUID matcher SERVICE_UUID_TEST if (result.getScanRecord().getServiceUuids() .contains(ParcelUuid.fromString(SERVICE_UUID_TEST))) { Log.v("DEBUG", "device: " + result.getDevice().getName() + "is Connected"); stopScanForDevice(); connecToGatt(result.getDevice()); } } } catch (Exception e) { e.toString(); } super.onScanResult(callbackType, result); } }; }