diff options
Diffstat (limited to 'pxp-monitor/src')
13 files changed, 2808 insertions, 0 deletions
diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertInformation.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertInformation.java new file mode 100644 index 0000000..af36c7b --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertInformation.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.bluetooth.BluetoothDevice; + +public class AlertInformation { + public BluetoothDevice device; + public String date; + public int rssi; +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertsHistory.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertsHistory.java new file mode 100644 index 0000000..f17c2e7 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertsHistory.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.app.ListActivity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Iterator; + +public class AlertsHistory extends ListActivity { + + private static final String TAG = AlertsHistory.class.getSimpleName(); + + private static final int REQUEST_ENABLE_BT = 1; + + private static AlertHistoryAdapter mAlertHistoryAdapter = null; + + private BluetoothAdapter mBluetoothAdapter = null; + + private PxpServiceProxy mPxpServiceProxy = null; + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mPxpServiceProxy = ((PxpServiceProxy.LocalBinder) service).getService(); + + if (mPxpServiceProxy == null) { + return; + } + listAlerts(); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpServiceProxy = null; + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getActionBar().setTitle("Alert History"); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(this, PxpServiceProxy.class); + + if (bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE) == false) { + Log.e(TAG, "Unable to bind"); + } + + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + + mAlertHistoryAdapter = new AlertHistoryAdapter(this); + setListAdapter(mAlertHistoryAdapter); + } + + @Override + protected void onResume() { + super.onResume(); + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + + if (mPxpServiceProxy != null) { + mAlertHistoryAdapter.clear(); + listAlerts(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + unbindService(mConnection); + mPxpServiceProxy = null; + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { + finish(); + return; + } + + if (mPxpServiceProxy != null) { + listAlerts(); + } + } + + private void listAlerts() { + Iterator<AlertInformation> iterator = + mPxpServiceProxy.mAlertHistory.iterator(); + + while (iterator.hasNext()) { + mAlertHistoryAdapter.addAlert(iterator.next()); + mAlertHistoryAdapter.notifyDataSetChanged(); + } + + } + + private class AlertHistoryAdapter extends BaseAdapter { + + private ArrayList<AlertInformation> mAlertInf; + + private LayoutInflater mInflater; + + private TextView mDeviceName; + + private TextView mDeviceAddress; + + private TextView mTime; + + private TextView mRssi; + + public AlertHistoryAdapter(Context context) { + super(); + + mAlertInf = new ArrayList<AlertInformation>(); + mInflater = LayoutInflater.from(context); + } + + public void addAlert(AlertInformation alert) { + if (!mAlertInf.contains(alert)) { + mAlertInf.add(alert); + } + } + + public void clear() { + mAlertInf.clear(); + this.notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mAlertInf.size(); + } + + @Override + public Object getItem(int i) { + return mAlertInf.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + + if (view == null) { + view = mInflater.inflate(R.layout.activity_alerts_history, null); + } + + mDeviceName = (TextView) view.findViewById(R.id.device_name); + mDeviceAddress = (TextView) view.findViewById(R.id.device_address); + mTime = (TextView) view.findViewById(R.id.alert_time); + mRssi = (TextView) view.findViewById(R.id.device_rssi); + + Log.e(TAG, "i: " + i); + + AlertInformation alert = mAlertInf.get(i); + + final String deviceName = alert.device.getName(); + + if (deviceName != null && deviceName.length() > 0) { + mDeviceName.setText(deviceName); + + } else { + mDeviceName.setText("Unknow device"); + } + + mDeviceAddress.setText(alert.device.getAddress()); + + mTime.setText(alert.date); + mRssi.setText("RSSI = " + String.valueOf(alert.rssi) + "dBm"); + + return view; + } + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectToDeviceDialogFragment.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectToDeviceDialogFragment.java new file mode 100644 index 0000000..b3e5ca2 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectToDeviceDialogFragment.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; + +public class ConnectToDeviceDialogFragment extends DialogFragment { + + private EditText mEditText; + + private ConnectToDeviceDialogListener mListener; + + public interface ConnectToDeviceDialogListener { + public void onConnectToDeviceDialogPositive(String address); + }; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + LayoutInflater inflater = getActivity().getLayoutInflater(); + + View view = inflater.inflate(R.layout.connect_to_device_dialog_fragment, null); + + mEditText = (EditText) view.findViewById(R.id.device_address); + + builder.setTitle(R.string.title); + builder.setView(view) + .setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + String address = mEditText.getText().toString();; + + mListener.onConnectToDeviceDialogPositive(address); + } + }) + .setNegativeButton(android.R.string.cancel, null); + return builder.create(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mListener = (MainActivity) activity; + } catch (ClassCastException e) { + throw new ClassCastException(this.getClass().getSimpleName() + + " can be only attached to " + MainActivity.class.getSimpleName()); + } + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectedDevicesActivity.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectedDevicesActivity.java new file mode 100644 index 0000000..4968123 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectedDevicesActivity.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.app.ListActivity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ConnectedDevicesActivity extends ListActivity { + + private static final String TAG = ConnectedDevicesActivity.class.getSimpleName(); + + public static final String DEVICE_CONNECTED_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; + + private static final int REQUEST_ENABLE_BT = 1; + + private ConnectedDeviceAdapter mConnectedDeviceAdapter; + + private BluetoothAdapter mBluetoothAdapter = null; + + private PxpServiceProxy mPxpServiceProxy = null; + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mPxpServiceProxy = ((PxpServiceProxy.LocalBinder) service).getService(); + + if (mPxpServiceProxy == null) { + return; + } + + listConnectedDevices(); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpServiceProxy = null; + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(this, PxpServiceProxy.class); + + if (bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE) == false) { + Log.e(TAG, "Unable to bind"); + } + + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + + mConnectedDeviceAdapter = new ConnectedDeviceAdapter(this); + setListAdapter(mConnectedDeviceAdapter); + } + + @Override + protected void onResume() { + super.onResume(); + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + + if (mPxpServiceProxy != null) { + mConnectedDeviceAdapter.clear(); + listConnectedDevices(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + unbindService(mConnection); + mPxpServiceProxy = null; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { + finish(); + return; + } + + if (mPxpServiceProxy != null) { + listConnectedDevices(); + } + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + + final BluetoothDevice leDevice = mConnectedDeviceAdapter.getDevice(position); + + if (leDevice == null) { + return; + } + + final Intent intent = new Intent(this, DeviceActivity.class); + + intent.setAction(DEVICE_CONNECTED_SELECTED); + intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(DeviceActivity.EXTRAS_DEVICE, leDevice); + + startActivity(intent); + } + + private synchronized void listConnectedDevices() { + + List<BluetoothDevice> connectedDevices = mPxpServiceProxy.getConnectedDevices(); + + Iterator<BluetoothDevice> iterator = connectedDevices.iterator(); + + while (iterator.hasNext()) { + mConnectedDeviceAdapter.addDevice(iterator.next()); + mConnectedDeviceAdapter.notifyDataSetChanged(); + } + } + + private class ConnectedDeviceAdapter extends BaseAdapter { + + private ArrayList<BluetoothDevice> mLeDevices; + + private LayoutInflater mInflater; + + private TextView mDeviceName; + + private TextView mDeviceAddress; + + public ConnectedDeviceAdapter(Context context) { + super(); + + mLeDevices = new ArrayList<BluetoothDevice>(); + mInflater = LayoutInflater.from(context); + } + + public void addDevice(BluetoothDevice device) { + + if (!mLeDevices.contains(device)) { + mLeDevices.add(device); + } + } + + public BluetoothDevice getDevice(int position) { + return mLeDevices.get(position); + } + + public void clear() { + mLeDevices.clear(); + this.notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mLeDevices.size(); + } + + @Override + public Object getItem(int i) { + return mLeDevices.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + + if (view == null) { + view = mInflater.inflate(R.layout.activity_list_devices, null); + } + + mDeviceName = (TextView) view.findViewById(R.id.device_name); + mDeviceAddress = (TextView) view.findViewById(R.id.device_address); + + BluetoothDevice device = mLeDevices.get(i); + + final String deviceName = device.getName(); + + if (deviceName != null && deviceName.length() > 0) { + mDeviceName.setText(deviceName); + + } else { + mDeviceName.setText("Unknow device"); + } + + mDeviceAddress.setText(device.getAddress()); + + return view; + } + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceActivity.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceActivity.java new file mode 100644 index 0000000..ab12551 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceActivity.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.util.Log; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Toast; + +public class DeviceActivity extends Activity { + + private static final String TAG = DeviceActivity.class.getSimpleName(); + + public static final String EXTRAS_DEVICE = "BLUETOOTH_DEVICE"; + + private static final int REQUEST_ENABLE_BT = 1; + + private BluetoothAdapter mBluetoothAdapter = null; + + protected PxpServiceProxy mPxpServiceProxy = null; + + protected BluetoothDevice mLeDevice = null; + + private DeviceInfoFragment mDeviceInfoFragment = null; + + private LinkLossFragment mLinkLossFragment = null; + + private PathLossFragment mPathLossFragment = null; + + private Uri mLinkNotification = null; + + private Uri mPathNotification = null; + + private Ringtone mLinkRingtone = null; + + private Ringtone mPathRingtone = null; + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "Connecting to service"); + mPxpServiceProxy = ((PxpServiceProxy.LocalBinder) service).getService(); + + if (mPxpServiceProxy == null) { + return; + } + + PxpServiceProxy.registerClient(mHandler); + + Intent intent = getIntent(); + String action = intent.getAction(); + + if (ConnectedDevicesActivity.DEVICE_CONNECTED_SELECTED. + equals(action)) { + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + + } else if (PairedDevicesActivity.DEVICE_PAIRED_SELECTED. + equals(action)) { + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + + if (mLeDevice != null && mPxpServiceProxy.connect(mLeDevice)) { + Log.d(TAG, "Pxp device connected"); + } + + } else if (ScanDevicesActivity.DEVICE_NOT_CONNECTED_SELECTED. + equals(action)) { + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + + if (mLeDevice != null && mPxpServiceProxy.connect(mLeDevice)) { + Log.d(TAG, "Pxp device connected"); + } + return; + + } else if (MainActivity.DEVICE_BY_ADDRESS_SELECTED. + equals(action)) { + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + + if (mLeDevice != null && mPxpServiceProxy.connect(mLeDevice)) { + Log.d(TAG, "Pxp device connected"); + } + } + + if (mLeDevice != null && mPxpServiceProxy.isPropertiesSet(mLeDevice)) { + setInfo(); + invalidateOptionsMenu(); + } + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpServiceProxy = null; + } + }; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + CharSequence toastText = null; + + switch (msg.what) { + + case PxpServiceProxy.MSG_DEVICE_CONNECTED: + Log.d(TAG, "PxpServiceProxy.MSG_DEVICE_CONNECTED"); + + mLeDevice = (BluetoothDevice) msg.obj; + + if (mLinkRingtone.isPlaying()) { + mLinkRingtone.stop(); + } + + this.removeMessages(PxpServiceProxy.MSG_LINK_LOSS_TIMEOUT); + + toastText = "Connected"; + + if (mPxpServiceProxy.isPropertiesSet(mLeDevice)) { + setInfo(); + } + break; + + case PxpServiceProxy.MSG_DEVICE_DISCONNECTED: + Log.d(TAG, "PxpServiceProxy.MSG_DEVICE_DISCONNECTED"); + + mLeDevice = (BluetoothDevice) msg.obj; + + if (mLeDevice.equals(mLeDevice)) { + toastText = "Disconnected"; + mLinkLossFragment.init(); + mPathLossFragment.init(); + } + break; + + case PxpServiceProxy.MSG_MISSING_LLS_SERVICE: + Log.d(TAG, "PxpServiceProxy.MSG_MISSING_LLS_SERVICE"); + toastText = "Device not connected - Link Loss Service not found"; + break; + + case PxpServiceProxy.MSG_LINKLOSS_ALERT: + Log.d(TAG, "PxpServiceProxy.MSG_LINKLOSS_ALERT"); + + int llsAlertValue = (Integer) msg.obj; + mLinkLossFragment.init(); + mPathLossFragment.init(); + + // No alerting if alertLevel == 0 + if (llsAlertValue == 0) { + return; + } + + this.sendMessageDelayed( + this.obtainMessage(PxpServiceProxy.MSG_LINK_LOSS_TIMEOUT), + PxpServiceProxy.LINK_LOSS_ALERT_TIMEOUT); + + mLinkRingtone.play(); + break; + + case PxpServiceProxy.MSG_PATHLOSS_ALERT: + if (!mPathRingtone.isPlaying()) { + mPathRingtone.play(); + } + break; + + case PxpServiceProxy.MSG_LINK_LOSS_TIMEOUT: + mLinkRingtone.stop(); + break; + + case PxpServiceProxy.MSG_ALERT_FINISH: + if (mPathRingtone.isPlaying()) { + mPathRingtone.stop(); + } + break; + + default: + super.handleMessage(msg); + } + + if (toastText != null) { + Toast toast = Toast.makeText(DeviceActivity.this, toastText, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + } + + invalidateOptionsMenu(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_device); + + mDeviceInfoFragment = (DeviceInfoFragment) getFragmentManager().findFragmentById( + R.id.device_info_fragment); + mLinkLossFragment = (LinkLossFragment) getFragmentManager().findFragmentById( + R.id.link_loss_fragment); + mPathLossFragment = (PathLossFragment) getFragmentManager().findFragmentById( + R.id.path_loss_fragment); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(this, PxpServiceProxy.class); + + if (bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE) == false) { + Log.e(TAG, "Unable to bind"); + } + + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + + mLinkNotification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); + mLinkRingtone = RingtoneManager.getRingtone(getApplicationContext(), mLinkNotification); + + mPathNotification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); + mPathRingtone = RingtoneManager.getRingtone(getApplicationContext(), mPathNotification); + + } + + @Override + protected void onResume() { + super.onResume(); + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + + if (mLeDevice != null && mPxpServiceProxy.isPropertiesSet(mLeDevice)) { + setInfo(); + invalidateOptionsMenu(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + if (mLinkRingtone.isPlaying()) { + mLinkRingtone.stop(); + } + + mLinkRingtone = null; + mLinkNotification = null; + + if (mPathRingtone.isPlaying()) { + mPathRingtone.stop(); + } + + mPathRingtone = null; + mPathNotification = null; + + unbindService(mConnection); + mPxpServiceProxy = null; + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { + finish(); + return; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.device, menu); + + if (!mPxpServiceProxy.isPropertiesSet(mLeDevice)) { + return false; + } + + if (mPxpServiceProxy.getConnectionState(mLeDevice)) { + menu.findItem(R.id.menu_disconnect).setVisible(true); + menu.findItem(R.id.menu_connect).setVisible(false); + + } else { + menu.findItem(R.id.menu_disconnect).setVisible(false); + menu.findItem(R.id.menu_connect).setVisible(true); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_disconnect: + Log.d(TAG, "Disconnecting"); + mPxpServiceProxy.disconnect(mLeDevice); + break; + + case R.id.menu_connect: + Log.d(TAG, "Connecting"); + if (mPxpServiceProxy.connect(mLeDevice) == true) { + setInfo(); + } + invalidateOptionsMenu(); + break; + } + return true; + } + + void setInfo() { + + final int LinkLossService = 0; + final int ImmediateAlertService = 1; + final int TxPowerService = 2; + + mDeviceInfoFragment.setDeviceInfo(mLeDevice.getName(), mLeDevice.getAddress()); + + mLinkLossFragment.onServiceFound(mPxpServiceProxy.checkServiceStatus(mLeDevice, + LinkLossService)); + + mPathLossFragment.onServiceFound( + mPxpServiceProxy.checkServiceStatus(mLeDevice, ImmediateAlertService), + mPxpServiceProxy.checkServiceStatus(mLeDevice, TxPowerService)); + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceInfoFragment.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceInfoFragment.java new file mode 100644 index 0000000..0174355 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceInfoFragment.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class DeviceInfoFragment extends Fragment { + + private TextView mDeviceName; + + private TextView mDeviceAddress; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.device_info_fragment, container, false); + + mDeviceName = ((TextView) view.findViewById(R.id.device_name)); + mDeviceAddress = ((TextView) view.findViewById(R.id.device_address)); + + return view; + } + + public void setDeviceInfo(String deviceName, String deviceAdress) { + mDeviceName.setText(deviceName); + mDeviceAddress.setText(deviceAdress); + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/LinkLossFragment.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/LinkLossFragment.java new file mode 100644 index 0000000..ce877d7 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/LinkLossFragment.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.app.Fragment; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; +import android.widget.ToggleButton; + +public class LinkLossFragment extends Fragment implements View.OnClickListener { + + private static final String TAG = LinkLossFragment.class.getSimpleName(); + + private DeviceActivity mActivity; + + private ToggleButton mLinkLossButtons[] = new ToggleButton[3]; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.link_loss_fragment, container, false); + + mLinkLossButtons[0] = (ToggleButton) view.findViewById(R.id.link_off); + mLinkLossButtons[1] = (ToggleButton) view.findViewById(R.id.link_mild); + mLinkLossButtons[2] = (ToggleButton) view.findViewById(R.id.link_high); + + for (ToggleButton btn : mLinkLossButtons) { + btn.setEnabled(false); + btn.setOnClickListener(this); + } + + return view; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mActivity = (DeviceActivity) activity; + } catch (ClassCastException e) { + throw new ClassCastException(this.getClass().getSimpleName() + + " can be only attached to " + MainActivity.class.getSimpleName()); + } + } + + public void init() { + + for (ToggleButton btn : mLinkLossButtons) { + btn.setEnabled(false); + } + } + + public void onServiceFound(boolean state) { + Log.d(TAG, "LLS service" + state); + + if (state) { + mLinkLossButtons[0].setEnabled(true); + mLinkLossButtons[1].setEnabled(true); + mLinkLossButtons[2].setEnabled(true); + + int linkLossLevel = 0; + + if (mActivity.mPxpServiceProxy.getLinkLossAlertLevel(mActivity.mLeDevice) < 0) { + mActivity.mPxpServiceProxy.setLinkLossAlertLevel(mActivity.mLeDevice, + linkLossLevel); + + } else { + linkLossLevel = mActivity.mPxpServiceProxy. + getLinkLossAlertLevel(mActivity.mLeDevice); + } + + for (int i = 0; i < mLinkLossButtons.length; i++) { + mLinkLossButtons[i].setChecked(linkLossLevel == i); + } + + } else { + init(); + Context context = getActivity(); + CharSequence text = "Link Loss Service not found"; + + Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + } + } + + @Override + public void onClick(View v) { + ToggleButton clickedButton = (ToggleButton) v; + + if (!mActivity.mPxpServiceProxy.getConnectionState(mActivity.mLeDevice)) { + return; + } + + int idx; + + switch (clickedButton.getId()) { + case R.id.link_off: + idx = 0; + break; + + case R.id.link_mild: + idx = 1; + break; + + case R.id.link_high: + idx = 2; + break; + + default: + return; + } + + for (int i = 0; i < mLinkLossButtons.length; i++) { + ToggleButton btn = mLinkLossButtons[i]; + + if (mLinkLossButtons[idx].isChecked()) { + + if (i != idx) { + btn.setChecked(false); + } + + } else { + btn.setChecked(i == 0); + idx = 0; + } + } + + mActivity.mPxpServiceProxy.setLinkLossAlertLevel(mActivity.mLeDevice, idx); + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/MainActivity.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/MainActivity.java new file mode 100644 index 0000000..63813a1 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/MainActivity.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.Menu; +import android.view.View; +import android.widget.Toast; + +import org.codeaurora.bluetooth.pxpmonitor.ConnectToDeviceDialogFragment.ConnectToDeviceDialogListener; + +public class MainActivity extends Activity implements ConnectToDeviceDialogListener { + + private static final String TAG = MainActivity.class.getSimpleName();; + + private static final int REQUEST_ENABLE_BT = 1; + + public static final String DEVICE_BY_ADDRESS_SELECTED = + "android.bluetooth.devicepicker.action.DEVICE_BY_ADDRESS_SELECTED"; + + protected BluetoothAdapter mBluetoothAdapter = null; + + protected PxpServiceProxy mPxpServiceProxy = null; + + protected Intent intent = null; + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mPxpServiceProxy = ((PxpServiceProxy.LocalBinder) service).getService(); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpServiceProxy = null; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Toast.makeText(this, "BLE not supported", Toast.LENGTH_SHORT).show(); + finish(); + } + + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(this, PxpServiceProxy.class); + + if (bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE) == false) { + Log.e(TAG, "Unable to bind"); + } + } + + @Override + protected void onResume() { + super.onResume(); + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { + finish(); + return; + } + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onDestroy() { + + if (mPxpServiceProxy != null) { + mPxpServiceProxy.disconnectDevices(); + } + + unbindService(mConnection); + mPxpServiceProxy = null; + super.onDestroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + public void searchForDevices(View view) { + final Intent intent = new Intent(this, ScanDevicesActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + startActivity(intent); + } + + public void showConnectedDevices(View view) { + final Intent intent = new Intent(this, ConnectedDevicesActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + startActivity(intent); + } + + public void showPairedDevices(View view) { + final Intent intent = new Intent(this, PairedDevicesActivity.class); + startActivity(intent); + } + + public void connectToDevice(View view) { + ConnectToDeviceDialogFragment newFragment = new ConnectToDeviceDialogFragment(); + newFragment.show(getFragmentManager(), "address"); + } + + public void showAlertHistory(View view) { + final Intent intent = new Intent(this, AlertsHistory.class); + intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + startActivity(intent); + } + + @Override + public void onConnectToDeviceDialogPositive(String address) { + BluetoothDevice leDevice = null; + + if (BluetoothAdapter.checkBluetoothAddress(address)) { + + leDevice = mBluetoothAdapter.getRemoteDevice(address); + + final Intent intent = new Intent(this, DeviceActivity.class); + intent.setAction(DEVICE_BY_ADDRESS_SELECTED); + intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(DeviceActivity.EXTRAS_DEVICE, leDevice); + startActivity(intent); + + } else { + Log.w(TAG, "Wrong device address"); + } + + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PairedDevicesActivity.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PairedDevicesActivity.java new file mode 100644 index 0000000..d010790 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PairedDevicesActivity.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.ListActivity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Set; + +public class PairedDevicesActivity extends ListActivity { + + private static final String TAG = PairedDevicesActivity.class.getSimpleName(); + + private static final int REQUEST_ENABLE_BT = 1; + + public static final String DEVICE_PAIRED_SELECTED = + "android.bluetooth.devicepicker.action.DEVICE_PAIRED_SELECTED"; + + private PairedDevicesListAdapter mPairedDeviceListAdapter; + + private BluetoothAdapter mAdapter = null; + + private PxpServiceProxy mPxpServiceProxy = null; + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "Connecting to service"); + mPxpServiceProxy = ((PxpServiceProxy.LocalBinder) service).getService(); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpServiceProxy = null; + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getActionBar().setTitle("Paired Devices"); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(this, PxpServiceProxy.class); + + if (bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE) == false) { + Log.e(TAG, "Unable to bind"); + } + + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mAdapter = bluetoothManager.getAdapter(); + + mPairedDeviceListAdapter = new PairedDevicesListAdapter(this); + setListAdapter(mPairedDeviceListAdapter); + getPairedDevices(); + } + + @Override + protected void onResume() { + super.onResume(); + + if (!mAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + + mPairedDeviceListAdapter.clear(); + getPairedDevices(); + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_ENABLE_BT) { + getPairedDevices(); + } + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + + final BluetoothDevice leDevice = mPairedDeviceListAdapter.getDevice(position); + + if (leDevice == null) { + return; + } + + Log.d(TAG, "Trying to connect to paired device :" + leDevice.getAddress()); + + final Intent intent = new Intent(this, DeviceActivity.class); + intent.setAction(DEVICE_PAIRED_SELECTED); + intent.putExtra(DeviceActivity.EXTRAS_DEVICE, leDevice); + startActivity(intent); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + unbindService(mConnection); + mPxpServiceProxy = null; + } + + private void getPairedDevices() { + Set<BluetoothDevice> devices = mAdapter.getBondedDevices(); + + for (BluetoothDevice device : devices) { + mPairedDeviceListAdapter.addDevice(device); + mPairedDeviceListAdapter.notifyDataSetChanged(); + } + } + + private class PairedDevicesListAdapter extends BaseAdapter { + + private ArrayList<BluetoothDevice> mLeDevices; + + private LayoutInflater mInflater; + + private TextView mDeviceName; + + private TextView mDeviceAddress; + + public PairedDevicesListAdapter(Context context) { + super(); + mLeDevices = new ArrayList<BluetoothDevice>(); + mInflater = LayoutInflater.from(context); + } + + public void addDevice(BluetoothDevice device) { + if (!mLeDevices.contains(device)) { + mLeDevices.add(device); + } + } + + public BluetoothDevice getDevice(int position) { + return mLeDevices.get(position); + } + + public void clear() { + mLeDevices.clear(); + this.notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mLeDevices.size(); + } + + @Override + public Object getItem(int i) { + return mLeDevices.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + + if (view == null) { + view = mInflater.inflate(R.layout.activity_list_devices, null); + } + + mDeviceName = (TextView) view.findViewById(R.id.device_name); + mDeviceAddress = (TextView) view.findViewById(R.id.device_address); + + BluetoothDevice device = mLeDevices.get(i); + + final String deviceName = device.getName(); + + if (deviceName != null && deviceName.length() > 0) { + mDeviceName.setText(deviceName); + + } else { + mDeviceName.setText("Unknown device"); + } + + mDeviceAddress.setText(device.getAddress()); + + return view; + } + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PathLossFragment.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PathLossFragment.java new file mode 100644 index 0000000..dc2f7ba --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PathLossFragment.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.app.Fragment; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; + +public class PathLossFragment extends Fragment implements View.OnClickListener { + + private static final String TAG = PathLossFragment.class.getSimpleName(); + + private static final short TX_POWER_MIN = -100; + + private static final short TX_POWER_MAX = 20; + + private static final short RSSI_MIN = -127; + + private static final short RSSI_MAX = 20; + + private static final short THRESHOLD_MIN = TX_POWER_MIN - RSSI_MAX; // -120 + + private static final short THRESHOLD_MAX = TX_POWER_MAX - RSSI_MIN; // 147 + + private DeviceActivity mActivity; + + private ToggleButton mPathLossButtons[] = new ToggleButton[3]; + + private EditText mMinValue; + + private EditText mMaxValue; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.path_loss_fragment, null, false); + + mPathLossButtons[0] = (ToggleButton) view.findViewById(R.id.path_off); + mPathLossButtons[1] = (ToggleButton) view.findViewById(R.id.path_mild); + mPathLossButtons[2] = (ToggleButton) view.findViewById(R.id.path_high); + mMinValue = (EditText) view.findViewById(R.id.threshold_min_value); + mMaxValue = (EditText) view.findViewById(R.id.threshold_max_value); + + mMinValue.setOnEditorActionListener(new EditText.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + + CharSequence text = null; + + if (actionId == EditorInfo.IME_ACTION_DONE) { + int threshold = Integer.parseInt(mMinValue.getText().toString()); + + if (threshold > THRESHOLD_MAX) { + + text = "Threshold is to big. Changing to " + + Integer.toString(THRESHOLD_MAX); + + threshold = THRESHOLD_MAX; + mMinValue.setText(String.valueOf(threshold)); + + } else if (threshold < THRESHOLD_MIN) { + + text = "Threshold is to small. Changing to " + + Integer.toString(THRESHOLD_MIN); + + threshold = THRESHOLD_MIN; + mMinValue.setText(String.valueOf(threshold)); + } + + if (text != null) { + Context context = getActivity(); + + Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + } + + mActivity.mPxpServiceProxy.setMinPathLossThreshold(mActivity.mLeDevice, + threshold); + + final InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getView().getWindowToken(), 0); + + return true; + } + return false; + } + }); + + mMaxValue.setOnEditorActionListener(new EditText.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + + CharSequence text = null; + + if (actionId == EditorInfo.IME_ACTION_DONE) { + int threshold = Integer.parseInt(mMaxValue.getText().toString()); + + if (threshold > THRESHOLD_MAX) { + + text = "Threshold is to big. Changing to " + + Integer.toString(THRESHOLD_MAX); + + threshold = THRESHOLD_MAX; + mMaxValue.setText(String.valueOf(threshold)); + + } else if (threshold < THRESHOLD_MIN) { + + text = "Threshold is to small. Changing to " + + Integer.toString(THRESHOLD_MIN); + + threshold = THRESHOLD_MIN; + mMaxValue.setText(String.valueOf(threshold)); + } + + if (text != null) { + Context context = getActivity(); + + Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + } + + mActivity.mPxpServiceProxy.setMaxPathLossThreshold(mActivity.mLeDevice, + threshold); + + final InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getView().getWindowToken(), 0); + + return true; + } + return false; + } + }); + + for (ToggleButton btn : mPathLossButtons) { + btn.setEnabled(false); + btn.setOnClickListener(this); + } + + return view; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mActivity = (DeviceActivity) activity; + } catch (ClassCastException e) { + throw new ClassCastException(this.getClass().getSimpleName() + + " can be only attached to " + MainActivity.class.getSimpleName()); + } + } + + public void init() { + + for (ToggleButton btn : mPathLossButtons) { + btn.setEnabled(false); + } + + mMinValue.setEnabled(false); + mMaxValue.setEnabled(false); + } + + public void onServiceFound(boolean llsService, boolean tpsService) { + + Log.v(TAG, "IAS service = " + llsService + " TPS service = " + tpsService); + + if (mActivity.mPxpServiceProxy.checkFailedReadTxPowerLevel(mActivity.mLeDevice)) { + Context context = getActivity(); + CharSequence text = "TX Power level not found"; + + Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.show(); + + } else if (llsService && tpsService) { + + mPathLossButtons[0].setEnabled(true); + mPathLossButtons[1].setEnabled(true); + mPathLossButtons[2].setEnabled(true); + mMinValue.setEnabled(true); + mMaxValue.setEnabled(true); + + int pathLossLevel; + + pathLossLevel = mActivity.mPxpServiceProxy + .getPathLossAlertLevel(mActivity.mLeDevice); + + for (int i = 0; i < mPathLossButtons.length; i++) { + mPathLossButtons[i].setChecked(pathLossLevel == i); + } + + int minThresholdValue; + int maxThresholdValue; + + minThresholdValue = mActivity.mPxpServiceProxy + .getMinPathLossThreshold(mActivity.mLeDevice); + + mMinValue.setText(String.format("%d", minThresholdValue)); + + maxThresholdValue = mActivity.mPxpServiceProxy + .getMaxPathLossThreshold(mActivity.mLeDevice); + + mMaxValue.setText(String.format("%d", maxThresholdValue)); + + } else { + init(); + } + + } + + @Override + public void onClick(View v) { + ToggleButton clickedButton = (ToggleButton) v; + + if (!mActivity.mPxpServiceProxy.getConnectionState(mActivity.mLeDevice)) { + return; + } + + int idx; + + switch (clickedButton.getId()) { + case R.id.path_off: + idx = 0; + break; + + case R.id.path_mild: + idx = 1; + break; + + case R.id.path_high: + idx = 2; + break; + + default: + return; + } + + for (int i = 0; i < mPathLossButtons.length; i++) { + ToggleButton btn = mPathLossButtons[i]; + + if (mPathLossButtons[idx].isChecked()) { + + if (i != idx) { + btn.setChecked(false); + } + + } else { + btn.setChecked(i == 0); + idx = 0; + } + } + + mActivity.mPxpServiceProxy.setPathLossAlertLevel(mActivity.mLeDevice, idx); + } +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PxpServiceProxy.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PxpServiceProxy.java new file mode 100644 index 0000000..8707ec7 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PxpServiceProxy.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Service; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import org.codeaurora.bluetooth.pxpservice.IPxpService; + +import java.util.ArrayList; +import java.util.List; + +public class PxpServiceProxy extends Service { + + private static final String TAG = PxpServiceProxy.class.getSimpleName(); + + public static final String DEVICE_CONNECTED = "android.bluetooth.action.DEVICE_CONNECTED"; + + public static final String DEVICE_DISCONNECTED = "android.bluetooth.action.DEVICE_DISCONNECTED"; + + public static final String MISSING_LLS_SERVICE = "android.bluetooth.action.DEVICE_MISSING_LLS_SERVICE"; + + public static final String LINKLOSS_ALERT = "android.bluetooth.action.LINKLOSS_ALERT"; + + public static final String DEVICE_ALLERT = "action.DEVICE_ALLERT"; + + public static final String PATHLOSS_ALLERT = "action.PATHLOSS_ALLERT"; + + public static final String PATHLOSS_ALLERT_FINISH = "action.ALLERT_FINISH"; + + public static final String EXTRAS_DEVICE = "BLUETOOTH_DEVICE"; + + public static final String EXTRAS_TIME = "ALERT_TIME"; + + public static final String EXTRAS_RSSI = "DEVICE_RSSI"; + + public static final String EXTRAS_LLS_ALERT_VALUE = "LLS_ALERT_VALUE"; + + public static final int MSG_REGISTER_CLIENT = 1; + + public static final int MSG_UNREGISTER_CLIENT = 2; + + public static final int MSG_DEVICE_CONNECTED = 1; + + public static final int MSG_MISSING_LLS_SERVICE = 2; + + public static final int MSG_LINKLOSS_ALERT = 3; + + public static final int MSG_DEVICE_DISCONNECTED = 4; + + public static final int MSG_LINK_LOSS_TIMEOUT = 5; + + public static final int MSG_PATHLOSS_ALERT = 6; + + public static final int MSG_ALERT_FINISH = 7; + + public static final int LINK_LOSS_ALERT_TIMEOUT = 15000; + + private IPxpService mPxpMonitorService = null; + + private static Handler mHandler = null; + + private final LocalBinder mBinder = new LocalBinder(); + + protected ArrayList<AlertInformation> mAlertHistory = null; + + public class LocalBinder extends Binder { + PxpServiceProxy getService() { + return PxpServiceProxy.this; + } + } + + public static void registerClient(Handler handler) { + mHandler = handler; + } + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mPxpMonitorService = IPxpService.Stub.asInterface((IBinder) service); + + Log.d(TAG, "Connected to Service"); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpMonitorService = null; + + Log.d(TAG, "Disconnected from Service"); + } + }; + + @Override + public void onCreate() { + super.onCreate(); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(); + gattServiceIntent.setAction("org.codeaurora.bluetooth.pxpservice.PxpMonitorService"); + bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE); + + mAlertHistory = new ArrayList<AlertInformation>(); + + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(DEVICE_ALLERT); + intentFilter.addAction(DEVICE_CONNECTED); + intentFilter.addAction(DEVICE_DISCONNECTED); + intentFilter.addAction(MISSING_LLS_SERVICE); + intentFilter.addAction(LINKLOSS_ALERT); + intentFilter.addAction(PATHLOSS_ALLERT); + intentFilter.addAction(PATHLOSS_ALLERT_FINISH); + + registerReceiver(mDataReceiver, intentFilter); + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy"); + super.onDestroy(); + + unregisterReceiver(mDataReceiver); + + Log.d(TAG, "UnbindService()"); + unbindService(mConnection); + mPxpMonitorService = null; + } + + @Override + public IBinder onBind(Intent arg0) { + Log.v(TAG, "onBind()"); + return mBinder; + } + + @Override + public boolean onUnbind(Intent intent) { + Log.v(TAG, "onUnbind()"); + return super.onUnbind(intent); + } + + public synchronized boolean connect(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.connect(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return false; + } + + public synchronized void disconnect(BluetoothDevice leDevice) { + try { + mPxpMonitorService.disconnect(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + + public synchronized int getLinkLossAlertLevel(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.getLinkLossAlertLevel(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return 0; + } + + public synchronized boolean setLinkLossAlertLevel(BluetoothDevice leDevice, int level) { + try { + return mPxpMonitorService.setLinkLossAlertLevel(leDevice, level); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return false; + } + + public synchronized int getPathLossAlertLevel(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.getPathLossAlertLevel(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return 0; + } + + public synchronized boolean setPathLossAlertLevel(BluetoothDevice leDevice, int level) { + try { + return mPxpMonitorService.setPathLossAlertLevel(leDevice, level); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + + return false; + } + + public synchronized int getMaxPathLossThreshold(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.getMaxPathLossThreshold(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return 0; + } + + public synchronized void setMaxPathLossThreshold(BluetoothDevice leDevice, int threshold) { + try { + mPxpMonitorService.setMaxPathLossThreshold(leDevice, threshold); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + + public synchronized int getMinPathLossThreshold(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.getMinPathLossThreshold(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return 0; + } + + public synchronized void setMinPathLossThreshold(BluetoothDevice leDevice, int threshold) { + try { + mPxpMonitorService.setMinPathLossThreshold(leDevice, threshold); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + + public synchronized boolean isPropertiesSet(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.isPropertiesSet(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return false; + } + + public synchronized boolean getConnectionState(BluetoothDevice leDevice) { + try { + + return mPxpMonitorService.getConnectionState(leDevice); + + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return false; + } + + public synchronized boolean checkServiceStatus(BluetoothDevice leDevice, int service) { + try { + return mPxpMonitorService.checkServiceStatus(leDevice, service); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return false; + } + + public synchronized boolean checkFailedReadTxPowerLevel(BluetoothDevice leDevice) { + try { + return mPxpMonitorService.checkFailedReadTxPowerLevel(leDevice); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return false; + } + + public synchronized void disconnectDevices() { + try { + mPxpMonitorService.disconnectDevices(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + + public synchronized List<BluetoothDevice> getConnectedDevices() { + try { + return mPxpMonitorService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return null; + } + + private final BroadcastReceiver mDataReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + BluetoothDevice mLeDevice; + String action = intent.getAction(); + + Message msg = null; + + if (DEVICE_CONNECTED.equals(action)) { + Log.d(TAG, "Acton = DEVICE_CONNECTED"); + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + Log.d(TAG, "After reading mLeDevice"); + msg = Message.obtain(null, + MSG_DEVICE_CONNECTED, mLeDevice); + + } else if (MISSING_LLS_SERVICE.equals(action)) { + Log.d(TAG, "Action = MISSING_LLS_SERVICE"); + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + msg = Message.obtain(null, + MSG_MISSING_LLS_SERVICE, mLeDevice); + + } else if (LINKLOSS_ALERT.equals(action)) { + Log.d(TAG, "Action = LINKLOSS_ALERT"); + mLeDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + int llsAlertValue = intent.getIntExtra(EXTRAS_LLS_ALERT_VALUE, 0); + + msg = Message.obtain(null, + MSG_LINKLOSS_ALERT, llsAlertValue); + + } else if (DEVICE_DISCONNECTED.equals(action)) { + Log.d(TAG, "Action = DEVICE_DISCONNECTED"); + mLeDevice = (BluetoothDevice) intent + .getParcelableExtra(EXTRAS_DEVICE); + msg = Message.obtain(null, + MSG_DEVICE_DISCONNECTED, mLeDevice); + + } else if (DEVICE_ALLERT.equals(action)) { + Log.d(TAG, "DEVICE_ALLERT"); + + AlertInformation alertInfo = new AlertInformation(); + alertInfo.device = (BluetoothDevice) intent.getParcelableExtra(EXTRAS_DEVICE); + alertInfo.date = intent.getStringExtra(EXTRAS_TIME); + alertInfo.rssi = intent.getIntExtra(EXTRAS_RSSI, 0); + mAlertHistory.add(alertInfo); + + } else if (PATHLOSS_ALLERT.equals(action)) { + Log.d(TAG, "PATHLOSS_ALLERT"); + + mLeDevice = (BluetoothDevice) intent + .getParcelableExtra(EXTRAS_DEVICE); + msg = Message.obtain(null, + MSG_PATHLOSS_ALERT, mLeDevice); + } else if (PATHLOSS_ALLERT_FINISH.equals(action)) { + Log.d(TAG, "DEVICE_ALLERT_FINISH"); + + mLeDevice = (BluetoothDevice) intent + .getParcelableExtra(EXTRAS_DEVICE); + msg = Message.obtain(null, + MSG_ALERT_FINISH, mLeDevice); + } + + if (msg == null) { + return; + } + + if (mHandler != null) { + mHandler.sendMessage(msg); + } + } + }; +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ScanDevicesActivity.java b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ScanDevicesActivity.java new file mode 100644 index 0000000..cfdf371 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ScanDevicesActivity.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpmonitor; + +import android.app.Activity; +import android.app.ListActivity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class ScanDevicesActivity extends ListActivity { + private static final String TAG = ScanDevicesActivity.class.getSimpleName(); + + public static final String DEVICE_NOT_CONNECTED_SELECTED = + "android.bluetooth.devicepicker.action.NOT_CONNECTED_DEVICE_SELECTED"; + + private static final int REQUEST_ENABLE_BT = 1; + + private BluetoothAdapter mBluetoothAdapter = null; + + private ScanDevicesListAdapter mDeviceListAdapter = null; + + private PxpServiceProxy mPxpServiceProxy = null; + + private Handler mHandler; + + private boolean mScanning; + + private static final long SCAN_PERIOD = 30000; // 30sec for now + + private final ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "Connecting to service"); + mPxpServiceProxy = ((PxpServiceProxy.LocalBinder) service).getService(); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mPxpServiceProxy = null; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getActionBar().setTitle("Scan for Devices"); + + Log.d(TAG, "On create: new intent BluetoothService"); + Intent gattServiceIntent = new Intent(this, PxpServiceProxy.class); + + if (bindService(gattServiceIntent, mConnection, BIND_AUTO_CREATE) == false) { + Log.e(TAG, "Unable to bind"); + } + + mHandler = new Handler(); + + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + + mDeviceListAdapter = new ScanDevicesListAdapter(this); + setListAdapter(mDeviceListAdapter); + scanDevice(true); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.scan, menu); + + if (mScanning) { + menu.findItem(R.id.menu_stop).setVisible(true); + menu.findItem(R.id.action_search).setVisible(false); + + } else { + menu.findItem(R.id.menu_stop).setVisible(false); + menu.findItem(R.id.action_search).setVisible(true); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_search: + mDeviceListAdapter.clear(); + scanDevice(true); + break; + + case R.id.menu_stop: + scanDevice(false); + break; + } + return true; + } + + @Override + protected void onResume() { + super.onResume(); + scanDevice(true); + } + + @Override + protected void onPause() { + super.onPause(); + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + + scanDevice(false); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { + finish(); + return; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + + unbindService(mConnection); + mPxpServiceProxy = null; + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + final BluetoothDevice leDevice = mDeviceListAdapter.getDevice(position); + + if (mScanning) { + scanDevice(false); + } + + final Intent intent = new Intent(this, DeviceActivity.class); + intent.setAction(DEVICE_NOT_CONNECTED_SELECTED); + intent.putExtra(DeviceActivity.EXTRAS_DEVICE, leDevice); + startActivity(intent); + } + + private void scanDevice(final boolean enable) { + + Runnable scan = new Runnable() { + + @Override + public void run() { + mScanning = false; + mBluetoothAdapter.stopLeScan(mLeScanCallback); + invalidateOptionsMenu(); + } + }; + + if (enable) { + mHandler.postDelayed(scan, SCAN_PERIOD); + mScanning = true; + mBluetoothAdapter.startLeScan(mLeScanCallback); + + } else { + mScanning = false; + mBluetoothAdapter.stopLeScan(mLeScanCallback); + mHandler.removeCallbacks(scan); + } + invalidateOptionsMenu(); + } + + private class ScanDevicesListAdapter extends BaseAdapter { + private ArrayList<BluetoothDevice> mLeDevices; + private LayoutInflater mInflater; + private Map<BluetoothDevice, DeviceInfo> mHashMapDevice; + private TextView mDeviceName; + private TextView mDeviceAddress; + private TextView mDeviceRssi; + private TextView mDeviceAdvData; + + public ScanDevicesListAdapter(Context context) { + super(); + mLeDevices = new ArrayList<BluetoothDevice>(); + mInflater = LayoutInflater.from(context); + mHashMapDevice = new HashMap<BluetoothDevice, DeviceInfo>(); + } + + public void addDevice(BluetoothDevice device, int rssi, String advData) { + + if (!mLeDevices.contains(device)) { + mLeDevices.add(device); + DeviceInfo deviceInfo = new DeviceInfo(rssi, advData); + mHashMapDevice.put(device, deviceInfo); + } + this.notifyDataSetChanged(); + } + + public BluetoothDevice getDevice(int position) { + return mLeDevices.get(position); + } + + public void clear() { + mLeDevices.clear(); + this.notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mLeDevices.size(); + } + + @Override + public Object getItem(int i) { + return mLeDevices.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + + if (view == null) { + view = mInflater.inflate(R.layout.activity_list_devices, null); + } + mDeviceName = (TextView) view.findViewById(R.id.device_name); + mDeviceAddress = (TextView) view.findViewById(R.id.device_address); + mDeviceRssi = (TextView) view.findViewById(R.id.device_rssi); + mDeviceAdvData = (TextView) view.findViewById(R.id.device_adv_data); + + BluetoothDevice device = mLeDevices.get(i); + + DeviceInfo deviceInfo = mHashMapDevice.get(device); + + final String deviceName = device.getName(); + + if (deviceName != null && deviceName.length() > 0) { + mDeviceName.setText(deviceName); + + } else { + mDeviceName.setText("Unknow device"); + } + + mDeviceAddress.setText(device.getAddress()); + mDeviceRssi.setText("RSSI = " + String.valueOf(deviceInfo.mRssi) + "dBm"); + mDeviceAdvData.setText("Adv data: " + deviceInfo.mAdvData); + + return view; + } + + private class DeviceInfo { + private int mRssi; + private String mAdvData; + + DeviceInfo(int rssi, String advData) { + mRssi = rssi; + mAdvData = advData; + } + } + } + + private BluetoothAdapter.LeScanCallback mLeScanCallback = + new BluetoothAdapter.LeScanCallback() { + + @Override + public void onLeScan(final BluetoothDevice device, final int rssi, + final byte[] scanRecord) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mDeviceListAdapter.addDevice(device, rssi, Arrays.toString(scanRecord)); + } + }); + } + }; +} diff --git a/pxp-monitor/src/org/codeaurora/bluetooth/pxpservice/IPxpService.aidl b/pxp-monitor/src/org/codeaurora/bluetooth/pxpservice/IPxpService.aidl new file mode 100644 index 0000000..d747819 --- /dev/null +++ b/pxp-monitor/src/org/codeaurora/bluetooth/pxpservice/IPxpService.aidl @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of The Linux Foundation nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.codeaurora.bluetooth.pxpservice; + +import android.bluetooth.BluetoothDevice; + +import java.util.List; + +interface IPxpService +{ + /** + * Connect to the GATT service hosted on proximity reporter. The result of + * connect operation is reported asynchronously through the Bluetooth GATT + * callback. + * + * @param leDevice Bluetooth device + * @return Return true if the connection where initiated successfully. + */ + boolean connect(in BluetoothDevice leDevice); + + /** + * Disconnect an existing connection with proximity reporter. The result of + * disconnect operation is reported asynchronously through the Bluetooth + * GATT callback. + * + * @param leDevice Bluetooth device + * @return void + */ + void disconnect(in BluetoothDevice leDevice); + + /** + * Write link loss alert level to the remote device. + * + * @param leDevice Bluetooth remote device + * @param level Alert level (0, 1 or 2). + * @return Return true if the write operation was successful. + */ + boolean setLinkLossAlertLevel(in BluetoothDevice leDevice, in int level); + + /** + * Read link loss alert level from the remote device. + * + * @param leDevice Bluetooth remote device + * @return Return value of link loss alert Level: 0 - off alert level 1 - + * mild alert level 2 - high alert level + */ + int getLinkLossAlertLevel(in BluetoothDevice leDevice); + + /** + * Set max value of path loss threshold. + * + * @param leDevice Bluetooth remote device + * @return void + */ + void setMaxPathLossThreshold(in BluetoothDevice leDevice, in int threshold); + + /** + * Get max path loss threshold value. + * + * @param leDevice Bluetooth remote device + * @return Return threshold value. + */ + int getMaxPathLossThreshold(in BluetoothDevice leDevice); + + /** + * Set max value of path loss threshold. + * + * @param leDevice Bluetooth remote device + * @return void + */ + void setMinPathLossThreshold(in BluetoothDevice leDevice, in int threshold); + + /** + * Get min path loss threshold value. + * + * @param leDevice Bluetooth remote device + * @return Return threshold value. + */ + int getMinPathLossThreshold(in BluetoothDevice leDevice); + + /** + * Write path loss alert level to the remote device. + * + * @param leDevice Bluetooth remote device + * @param level Alert level (0, 1 or 2). + * @return Return true if the write operation where initiated successfully. + */ + boolean setPathLossAlertLevel(in BluetoothDevice leDevice, in int level); + + /** + * Read path loss alert level from the remote device. + * + * @param leDevice Bluetooth remote device + * @return Return value path loss alert Level: 0 - off alert level 1 - mild + * alert level 2 - high alert level + */ + int getPathLossAlertLevel(in BluetoothDevice leDevice); + + /** + * Get current connection state for a given device. + * + * @param leDevice Bluetooth remote device + * @return true if device is connected + */ + boolean getConnectionState(in BluetoothDevice leDevice); + + /** + * Check if variable deviceProp for current device is set. + * + * @param leDevice Bluetooth remote device + * @return true if is set + */ + boolean isPropertiesSet(in BluetoothDevice leDevice); + + /** + * Check if remote service was found on a given device + * + * @param leDevice Bluetooth remote device + * @param service remote service + * @return true if remote service was found + */ + boolean checkServiceStatus(in BluetoothDevice leDevice, in int service); + + /** + * Check status of reading Tx Power level characteristic operation. + * + * @param leDevice Bluetooth remote device + * @return true if reading operation failed. + */ + boolean checkFailedReadTxPowerLevel(in BluetoothDevice leDevice); + + /** + * Disconnect all connected devices. + */ + void disconnectDevices(); + + /** + * List connected devices to the proximity monitor. + * + * @return Return List<BluetoothDevice> of the connected devices. + */ + List<BluetoothDevice> getConnectedDevices(); +} + |