/* * 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 com.eveningoutpost.dexdrip.Services; import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; 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.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.PowerManager; import android.preference.PreferenceManager; import android.util.Log; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.ForegroundServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.HM10Attributes; import com.eveningoutpost.dexdrip.Models.TransmitterData; import java.util.Calendar; import java.util.Date; import java.util.UUID; @TargetApi(Build.VERSION_CODES.KITKAT) public class DexCollectionService extends Service { private final static String TAG = DexCollectionService.class.getSimpleName(); private String mDeviceName; private String mDeviceAddress; private boolean is_connected = false; SharedPreferences prefs; public DexCollectionService dexCollectionService; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; private ForegroundServiceStarter foregroundServiceStarter; private int mConnectionState = STATE_DISCONNECTED; private BluetoothDevice device; private Context mContext = null; private static final int STATE_DISCONNECTED = BluetoothProfile.STATE_DISCONNECTED; private static final int STATE_DISCONNECTING = BluetoothProfile.STATE_DISCONNECTING; private static final int STATE_CONNECTING = BluetoothProfile.STATE_CONNECTING; private static final int STATE_CONNECTED = BluetoothProfile.STATE_CONNECTED; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static UUID xDripDataService = UUID.fromString(HM10Attributes.HM_10_SERVICE); public final static UUID xDripDataCharacteristic = UUID.fromString(HM10Attributes.HM_RX_TX); @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { foregroundServiceStarter = new ForegroundServiceStarter(getApplicationContext(), this); foregroundServiceStarter.start(); mContext = getApplicationContext(); dexCollectionService = this; prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); listenForChangeInSettings(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){ stopSelf(); return START_NOT_STICKY; } if (CollectionServiceStarter.isBTWixel(getApplicationContext())) { setFailoverTimer(); } else { stopSelf(); return START_NOT_STICKY; } attemptConnection(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); close(); foregroundServiceStarter.stop(); setRetryTimer(); Log.w(TAG, "SERVICE STOPPED"); } public SharedPreferences.OnSharedPreferenceChangeListener prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if(key.compareTo("run_service_in_foreground") == 0) { Log.e("FOREGROUND", "run_service_in_foreground changed!"); if (prefs.getBoolean("run_service_in_foreground", false)) { foregroundServiceStarter = new ForegroundServiceStarter(getApplicationContext(), dexCollectionService); foregroundServiceStarter.start(); Log.w(TAG, "Moving to foreground"); } else { dexCollectionService.stopForeground(true); Log.w(TAG, "Removing from foreground"); } } } }; public void listenForChangeInSettings() { prefs.registerOnSharedPreferenceChangeListener(prefListener); } public void setRetryTimer() { if (CollectionServiceStarter.isBTWixel(getApplicationContext())) { long retry_in = (1000 * 60 * 2); Log.d(TAG, "Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.setExact(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, DexCollectionService.class), 0)); } } public void setFailoverTimer() { //Sometimes it gets stuck in limbo on 4.4, this should make it try again if (CollectionServiceStarter.isBTWixel(getApplicationContext())) { long retry_in = (1000 * 60 * 5); Log.d(TAG, "Fallover Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.set(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, DexCollectionService.class), 0)); } else { stopSelf(); } } public void attemptConnection() { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager != null) { mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter != null) { if (device != null) { mConnectionState = STATE_DISCONNECTED; for (BluetoothDevice bluetoothDevice : mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT)) { if (bluetoothDevice.getAddress().compareTo(device.getAddress()) == 0) { mConnectionState = STATE_CONNECTED; } } } Log.w(TAG, "Connection state: " + mConnectionState); if (mConnectionState == STATE_DISCONNECTED || mConnectionState == STATE_DISCONNECTING) { ActiveBluetoothDevice btDevice = ActiveBluetoothDevice.first(); if (btDevice != null) { mDeviceName = btDevice.name; mDeviceAddress = btDevice.address; if (mBluetoothAdapter.isEnabled() && mBluetoothAdapter.getRemoteDevice(mDeviceAddress) != null) { connect(mDeviceAddress); return; } } } else if (mConnectionState == STATE_CONNECTED) { //WOOO, we are good to go, nothing to do here! Log.w(TAG, "Looks like we are already connected, going to read!"); return; } } } setRetryTimer(); } private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { mConnectionState = STATE_CONNECTED; ActiveBluetoothDevice.connected(); Log.w(TAG, "Connected to GATT server."); mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { mConnectionState = STATE_DISCONNECTED; ActiveBluetoothDevice.disconnected(); Log.w(TAG, "Disconnected from GATT server."); setRetryTimer(); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattService gattService = mBluetoothGatt.getService(xDripDataService); if (gattService != null) { BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(xDripDataCharacteristic); if (gattCharacteristic != null ) { final int charaProp = gattCharacteristic.getProperties(); if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { mBluetoothGatt.setCharacteristicNotification(gattCharacteristic, true); } else { Log.e(TAG, "characteristic " + gattCharacteristic.getUuid() + " doesn't have notify properties"); } } else { Log.e(TAG, "characteristic " + xDripDataCharacteristic + " not found"); } } else { Log.e(TAG, "service " + xDripDataCharacteristic + " not found"); } } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { setSerialDataToTransmitterRawData(data, data.length); } } }; public boolean connect(final String address) { Log.w(TAG, "going to connect to device at address" + address); if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); setRetryTimer(); return false; } if (mBluetoothGatt != null) { Log.w(TAG, "BGatt isnt null, Closing."); mBluetoothGatt.close(); mBluetoothGatt = null; } device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); setRetryTimer(); return false; } Log.w(TAG, "Trying to create a new connection."); mBluetoothGatt = device.connectGatt(getApplicationContext(), true, mGattCallback); mConnectionState = STATE_CONNECTING; return true; } public void disconnect() { if ( mBluetoothGatt == null) { return; } mBluetoothGatt.disconnect(); Log.d(TAG, "Gatt Disconnect"); } public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); setRetryTimer(); mBluetoothGatt = null; mConnectionState = STATE_DISCONNECTED; } public void setSerialDataToTransmitterRawData(byte[] buffer, int len) { Log.w(TAG, "received some data!"); PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ReceivedReading"); wakeLock.acquire(); Long timestamp = new Date().getTime(); TransmitterData transmitterData = TransmitterData.create(buffer, len, timestamp); if (transmitterData != null) { Sensor sensor = Sensor.currentSensor(); if (sensor != null) { sensor.latest_battery_level = transmitterData.sensor_battery_level; sensor.save(); BgReading.create(transmitterData.raw_data, this, timestamp); } else { Log.w(TAG, "No Active Sensor, Data only stored in Transmitter Data"); } } wakeLock.release(); } }