summaryrefslogtreecommitdiffstats
path: root/pxp-monitor/src
diff options
context:
space:
mode:
Diffstat (limited to 'pxp-monitor/src')
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertInformation.java37
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/AlertsHistory.java235
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectToDeviceDialogFragment.java87
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ConnectedDevicesActivity.java247
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceActivity.java365
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/DeviceInfoFragment.java60
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/LinkLossFragment.java165
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/MainActivity.java188
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PairedDevicesActivity.java236
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PathLossFragment.java299
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/PxpServiceProxy.java393
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpmonitor/ScanDevicesActivity.java326
-rw-r--r--pxp-monitor/src/org/codeaurora/bluetooth/pxpservice/IPxpService.aidl170
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();
+}
+