From 54d4b66174dd0ce4d5c7e8d3898a2b4d3893dc71 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 1 Sep 2016 14:19:28 -0700 Subject: MAP MCE Add MAP client code into packages/apps/Bluetooth. Changes here are to define the MAP MCE interface and enable its selection when running on a device that is also running a PBAP client (Car Kitt). Bug: 30467210 Change-Id: Ifa2cdea7d67f63a2b5f3d971df8ec6d321dc5fee (cherry picked from commit fae5ff2578fe1e51a4677e6d344d4f5ff4618ed1) --- .../java/android/bluetooth/BluetoothMapClient.java | 415 +++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothMapClient.java (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java new file mode 100644 index 0000000000..da66b93f2d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the APIs to control the Bluetooth MAP MCE Profile. + * + * @hide + */ +public final class BluetoothMapClient implements BluetoothProfile { + + private static final String TAG = "BluetoothMapClient"; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; + public static final String ACTION_MESSAGE_RECEIVED = + "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; + /* Actions to be used for pending intents */ + public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = + "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; + public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = + "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; + + private IBluetoothMapClient mService; + private final Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + + /** There was an error trying to obtain the state */ + public static final int STATE_ERROR = -1; + + public static final int RESULT_FAILURE = 0; + public static final int RESULT_SUCCESS = 1; + /** Connection canceled before completion. */ + public static final int RESULT_CANCELED = 2; + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (VDBG) Log.d(TAG, "Unbinding service..."); + synchronized (mConnection) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } + } + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + doBind(); + } + } catch (Exception re) { + Log.e(TAG, "", re); + } + } + } + } + }; + + /** + * Create a BluetoothMapClient proxy object. + */ + /*package*/ BluetoothMapClient(Context context, ServiceListener l) { + if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothMapClient.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); + return false; + } + return true; + } + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothMap will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public void close() { + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } + } + } + mServiceListener = null; + } + + /** + * Returns true if the specified Bluetooth device is connected. + * Returns false if not connected, or if this proxy object is not + * currently connected to the Map service. + */ + public boolean isConnected(BluetoothDevice device) { + if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); + if (mService != null) { + try { + return mService.isConnected(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Initiate connection. Initiation of outgoing connections is not + * supported for MAP server. + */ + public boolean connect(BluetoothDevice device) { + if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); + if (mService != null) { + try { + return mService.connect(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Initiate disconnect. + * + * @param device Remote Bluetooth Device + * @return false on error, true otherwise + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) Log.d(TAG, "disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the list of connected devices. Currently at most one. + * + * @return list of connected devices + */ + @Override + public List getConnectedDevices() { + if (DBG) Log.d(TAG, "getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } + + /** + * Get the list of devices matching specified states. Currently at most one. + * + * @return list of matching devices + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } + + /** + * Get connection state of device + * + * @return device connection state + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Set priority of the profile + * + *

The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or + * {@link #PRIORITY_OFF}, + * + * @param device Paired bluetooth device + * @return true if priority is set, false on error + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return mService.setPriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the priority of the profile. + * + *

The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * @param device Bluetooth device + * @return priority of the device + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return PRIORITY_OFF; + } + + /** + * Send a message. + * + * Send an SMS message to either the contacts primary number or the telephone number specified. + * + * @param device Bluetooth device + * @param contacts Uri[] of the contacts + * @param message Message to be sent + * @param sentIntent intent issued when message is sent + * @param deliveredIntent intent issued when message is delivered + * @return true if the message is enqueued, false on error + */ + public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, + PendingIntent sentIntent, PendingIntent deliveredIntent) { + if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + /** + * Get unread messages. Unread messages will be published via {@LINK #ACTION_MESSAGE_RECEIVED}. + * + * @param device Bluetooth device + * @return true if the message is enqueued, false on error + */ + public boolean getUnreadMessages(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getUnreadMessages(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothMapClient.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, + BluetoothMapClient.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT); + } + } + }; + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; + if (DBG) Log.d(TAG, "Bluetooth is Not enabled"); + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + +} -- cgit v1.2.3 From d95b0847a70845532b12b5fab0290dcc22be29b3 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 6 Oct 2016 11:44:53 -0700 Subject: MAP MCE Fix @LINK to @link in javadoc. Bug: 30467210 Change-Id: Icac176947bee971c3f5d11fd4166cf8ceb0a437e (cherry picked from commit 584bb127754b2826e8a540e23acb2a6c398e6432) --- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index da66b93f2d..425248224e 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -360,7 +360,7 @@ public final class BluetoothMapClient implements BluetoothProfile { } /** - * Get unread messages. Unread messages will be published via {@LINK #ACTION_MESSAGE_RECEIVED}. + * Get unread messages. Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}. * * @param device Bluetooth device * @return true if the message is enqueued, false on error -- cgit v1.2.3 From 8ba9f62d6ed501b221ae66b89ee2dacdf13c411f Mon Sep 17 00:00:00 2001 From: Srinivas Visvanathan Date: Fri, 3 Mar 2017 09:57:18 -0800 Subject: Defining extras for New Unread-Message Broadcast - Defining extras for sender fields in the Broadcast intent. Bug: 33280056 Test: Manually Change-Id: Ie77bee498141c079f6f2ec811e527230c95e8831 --- framework/java/android/bluetooth/BluetoothMapClient.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 425248224e..7d8459cb48 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -50,6 +50,12 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; + /* Extras used in ACTION_MESSAGE_RECEIVED intent */ + public static final String EXTRA_SENDER_CONTACT_URI = + "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; + public static final String EXTRA_SENDER_CONTACT_NAME = + "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; + private IBluetoothMapClient mService; private final Context mContext; private ServiceListener mServiceListener; -- cgit v1.2.3 From 5db7a4c0968b63f99cdcb24a6520352d2817c92e Mon Sep 17 00:00:00 2001 From: Srinivas Visvanathan Date: Tue, 7 Mar 2017 10:22:53 -0800 Subject: Adding handle extra in unread-message broadcast - Handle will be useful for messaging apps. Bug: 33280056 Test: Manually Change-Id: Id4b1bf2d90d9eaea1504cc270a922a1d6f1c468b --- framework/java/android/bluetooth/BluetoothMapClient.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 7d8459cb48..ccab3cdf0b 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -50,7 +50,10 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; - /* Extras used in ACTION_MESSAGE_RECEIVED intent */ + /* Extras used in ACTION_MESSAGE_RECEIVED intent. + * NOTE: HANDLE is only valid for a single session with the device. */ + public static final String EXTRA_MESSAGE_HANDLE = + "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; public static final String EXTRA_SENDER_CONTACT_URI = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; public static final String EXTRA_SENDER_CONTACT_NAME = -- cgit v1.2.3 From 910201beb0bde1dcf6b33e4ec5d1eb60042419d8 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 22 Aug 2017 16:06:54 -0700 Subject: Fix checkstyle errors (1/2) * Automatic style corrections through IDE Bug: 63596319 Test: make checkbuild, no manual changes, no functional changes Change-Id: I2397d55abc34c9b7a9b748bec6137778df3421a7 --- framework/java/android/bluetooth/BluetoothMapClient.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ccab3cdf0b..09dd5ad62b 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -347,10 +347,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * * Send an SMS message to either the contacts primary number or the telephone number specified. * - * @param device Bluetooth device - * @param contacts Uri[] of the contacts - * @param message Message to be sent - * @param sentIntent intent issued when message is sent + * @param device Bluetooth device + * @param contacts Uri[] of the contacts + * @param message Message to be sent + * @param sentIntent intent issued when message is sent * @param deliveredIntent intent issued when message is delivered * @return true if the message is enqueued, false on error */ @@ -393,7 +393,7 @@ public final class BluetoothMapClient implements BluetoothProfile { mService = IBluetoothMapClient.Stub.asInterface(service); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, - BluetoothMapClient.this); + BluetoothMapClient.this); } } -- cgit v1.2.3 From 9e045d26d0128826b40520f523307d8d16473779 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 22 Aug 2017 21:21:23 -0700 Subject: Fix checkstyle errors (2/2) * Manual style corrections with IDE assistance * Variable name refactors are done through IDE * Corrected general style errors such as: - "final private var" -> "private final var" - "&&", "+", "||" should not be at the end of line - Non-static private variable should be like "mVar" - Private static variable should be like "sVar" - Code file should always end with newline - Inherited methods should be annotated with @Override and no @hide tags - Public methods should always have a JavaDoc entry - "int[] array" is preferred over "int array[]" - private methods should be accessed without "this." when there is no name collisions. - "boolean ? true : false" -> boolean - "boolean ? false : true" -> !boolean - "boolean == true" OR "boolean != false" -> boolean - "boolean != true" OR "boolean == false" -> !boolean Bug: 63596319 Test: make checkbuild, no functional changes Change-Id: Iabdc2be912a32dd63a53213d175cf1bfef268ccd --- .../java/android/bluetooth/BluetoothMapClient.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 09dd5ad62b..3e0c36548c 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -72,7 +72,7 @@ public final class BluetoothMapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -216,8 +216,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -276,8 +275,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -300,10 +298,9 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + if (mService != null && isEnabled() && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { return false; } try { @@ -329,8 +326,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { -- cgit v1.2.3 From 1f686f645294d624f8b61e4761fd14787a496c75 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 17 Aug 2017 12:11:18 -0700 Subject: Bluetooth: Thread-safe binder invocation * Binder object may become null between null check and actual invocation if using a instance private variable assignable by service connection callbacks * The solution to this problem without locking is to assign existing binder variable to a local final variable before the null check * Any further invocation to a disconnected binder object will result in RemoteException that is caught by the try-catch block * Read and write to volatile variable is always atomic and hence thread-safe * Removed unnecessary synchronization in BluetoothAdapter constructor * Private mConnection objects should be final * Simplfied several return statements where booleans can be returned directly * Removed unnecessary catches for NPE since there won't be any Bug: 64724692 Test: make, pair and use devices, no functional change Change-Id: Ifc9d6337c0d451a01484b61243230725d5314f8e --- .../java/android/bluetooth/BluetoothMapClient.java | 72 ++++++++++++---------- 1 file changed, 39 insertions(+), 33 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 3e0c36548c..af3b662d6a 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -59,7 +59,7 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; - private IBluetoothMapClient mService; + private volatile IBluetoothMapClient mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -176,9 +176,10 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothMapClient service = mService; + if (service != null) { try { - return mService.isConnected(device); + return service.isConnected(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -195,9 +196,10 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); - if (mService != null) { + final IBluetoothMapClient service = mService; + if (service != null) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -216,14 +218,15 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -235,15 +238,16 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); } @@ -255,15 +259,16 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); } @@ -275,15 +280,16 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } @@ -298,19 +304,20 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } try { - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -326,15 +333,16 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getPriority(device); + return service.getPriority(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return PRIORITY_OFF; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return PRIORITY_OFF; } @@ -353,9 +361,10 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent); + return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -372,9 +381,10 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getUnreadMessages(device); + return service.getUnreadMessages(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -409,12 +419,8 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } - private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; - - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + private static boolean isValidDevice(BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } -- cgit v1.2.3 From 6edd25f33e4e4bed4fdd9eb815012111e59ad2a7 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 2 Feb 2018 13:25:31 -0700 Subject: Pass in the user defined by Context. The majority of Manager-style classes already use Context.getUserId() when making calls into the OS, so clean up the remaining callers to unify behind this strategy. This gives @SystemApi developers a nice clean interface to interact across user boundaries, instead of manually adding "AsUser" or "ForUser" method variants, which would quickly become unsustainable. Test: builds, boots Bug: 72863821 Exempt-From-Owner-Approval: trivial changes Change-Id: Ib772ec4438e57a2ad4950821b9432f9842998451 --- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index af3b662d6a..4f21d936e5 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -125,7 +125,7 @@ public final class BluetoothMapClient implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - android.os.Process.myUserHandle())) { + mContext.getUser())) { Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); return false; } -- cgit v1.2.3 From 7d543894e0497651fc160728d659543483500f87 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 1 Aug 2018 15:07:20 +0100 Subject: Add @UnsupportedAppUsage annotations For packages: android.bluetooth.le android.bluetooth This is an automatically generated CL. See go/UnsupportedAppUsage for more details. Exempted-From-Owner-Approval: Mechanical changes to the codebase which have been approved by Android API council and announced on android-eng@ Bug: 110868826 Test: m Change-Id: Ifcf24c0617acd7facc0e03f30a95c3a6b09b205c Merged-In: I88a1311e27c5f9a5f9d1035db76034f86f650efc --- framework/java/android/bluetooth/BluetoothMapClient.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index af3b662d6a..bff67f2980 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -358,6 +359,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @param deliveredIntent intent issued when message is delivered * @return true if the message is enqueued, false on error */ + @UnsupportedAppUsage public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); -- cgit v1.2.3 From a06a03302abf47167c40202414750d9c9ecfd80b Mon Sep 17 00:00:00 2001 From: Vasu Nori Date: Fri, 17 Aug 2018 17:25:28 -0700 Subject: Add hidden API to return "Uploading" bit value from SDP record's MapSupportedFeatures. Bug: 111614861 Test: tested w/ KitchenSink App Change-Id: I43895183d7b315f57257e1d2045f17dedcb0cfcd --- .../java/android/bluetooth/BluetoothMapClient.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 183be5f38b..559a59b68b 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -73,6 +73,8 @@ public final class BluetoothMapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; + private static final int UPLOADING_FEATURE_BITMASK = 0x08; + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { @@ -395,6 +397,23 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } + /** + * Returns the "Uploading" feature bit value from the SDP record's + * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114). + * @param device The Bluetooth device to get this value for. + * @return Returns true if the Uploading bit value in SDP record's + * MapSupportedFeatures field is set. False is returned otherwise. + */ + public boolean isUploadingSupported(BluetoothDevice device) { + try { + return (mService != null && isEnabled() && isValidDevice(device)) + && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + return false; + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); -- cgit v1.2.3 From 1652b94a121a19863c554551c57406cbef504ce8 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 26 Mar 2019 21:38:08 +0800 Subject: Fix binder leakage when turning off Bluetooth * In current design, Bluetooth AdapterState stops all BR/EDR profiles' service and triggers onServiceDisconnected callback to all binder clients before BluetoothManagerService invokes onBluetoothStateChange(false), which means unbind service would never be called in framework. * Do unbind service when onServiceDisconnected is invoked. * Move profile binder logic to BluetoothProfileConnector except: - BluetoothHeadset: its binder logic is in BluetoothManagerService - BluetoothPbap: it has an individual ServiceListener Bug: 129037442 Bug: 129437895 Test: Bluetooth ON/OFF stress test. adb shell dumpsys activity services | egrep "com.android.bluetooth" to check whether AppBindRecord for com.android.bluetooth grows Merged-In: Id0d85866d386962b94d2d966f0a864b1da165d13 Change-Id: Id0d85866d386962b94d2d966f0a864b1da165d13 --- .../java/android/bluetooth/BluetoothMapClient.java | 137 +++++---------------- 1 file changed, 28 insertions(+), 109 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 559a59b68b..ec0180c5ad 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -18,11 +18,9 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.net.Uri; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -60,11 +58,6 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; - private volatile IBluetoothMapClient mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -75,64 +68,23 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final int UPLOADING_FEATURE_BITMASK = 0x08; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, + "BluetoothMapClient", IBluetoothMapClient.class.getName()) { + @Override + public IBluetoothMapClient getServiceInterface(IBinder service) { + return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMapClient proxy object. */ - /*package*/ BluetoothMapClient(Context context, ServiceListener l) { + /*package*/ BluetoothMapClient(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothMapClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { - Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -150,26 +102,11 @@ public final class BluetoothMapClient implements BluetoothProfile { * are ok. */ public void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothMapClient getService() { + return mProfileConnector.getService(); } /** @@ -179,7 +116,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null) { try { return service.isConnected(device); @@ -199,7 +136,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null) { try { return service.connect(device); @@ -221,7 +158,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -241,7 +178,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -262,7 +199,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -283,7 +220,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -307,7 +244,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -336,7 +273,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -365,7 +302,7 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); @@ -385,7 +322,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getUnreadMessages(device); @@ -405,34 +342,16 @@ public final class BluetoothMapClient implements BluetoothProfile { * MapSupportedFeatures field is set. False is returned otherwise. */ public boolean isUploadingSupported(BluetoothDevice device) { + final IBluetoothMapClient service = getService(); try { - return (mService != null && isEnabled() && isValidDevice(device)) - && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + return (service != null && isEnabled() && isValidDevice(device)) + && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); } catch (RemoteException e) { Log.e(TAG, e.getMessage()); } return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothMapClient.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, - BluetoothMapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT); - } - } - }; - private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; -- cgit v1.2.3 From 1fd7ddfb8581f383c9bb45e510bedd2b5e397113 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Tue, 14 May 2019 11:09:19 -0700 Subject: Add timestamp and read status extra constants to MAP Client Bug: b/132455654 Test: build and run on automotive hardware. Use kitchen sink to send and receive messages. Change-Id: Ic0f04640f1894d6cf336ba7e641df9cf148a2bbd Merged-In: Ic0f04640f1894d6cf336ba7e641df9cf148a2bbd --- framework/java/android/bluetooth/BluetoothMapClient.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ec0180c5ad..69682c6ab5 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -53,6 +53,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * NOTE: HANDLE is only valid for a single session with the device. */ public static final String EXTRA_MESSAGE_HANDLE = "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; + public static final String EXTRA_MESSAGE_TIMESTAMP = + "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP"; + public static final String EXTRA_MESSAGE_READ_STATUS = + "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS"; public static final String EXTRA_SENDER_CONTACT_URI = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; public static final String EXTRA_SENDER_CONTACT_NAME = -- cgit v1.2.3 From e8bac9b871a87aa38e99956c380069c3c377eb79 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 27 Nov 2019 18:09:33 -0800 Subject: Rename priority to connection policy in bluetooth apis Bug: 145005327 Test: Manual Change-Id: I43ad57feb7dd70f39005ad7a01bc7dac6fb7b639 --- .../java/android/bluetooth/BluetoothMapClient.java | 64 ++++++++++++++++++---- 1 file changed, 54 insertions(+), 10 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 69682c6ab5..0ec473c85a 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -16,6 +16,9 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.Context; @@ -240,22 +243,44 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Set priority of the profile * - *

The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or - * {@link #PRIORITY_OFF}, + *

The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, * * @param device Paired bluetooth device + * @param priority * @return true if priority is set, false on error + * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); + return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + } + + /** + * Set connection policy of the profile + * + *

The device should already be paired. + * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, + * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF - && priority != BluetoothProfile.PRIORITY_ON) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } try { - return service.setPriority(device, priority); + return service.setConnectionPolicy(device, connectionPolicy); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -269,25 +294,44 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the priority of the profile. * *

The priority can be any of: - * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, - * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} * * @param device Bluetooth device * @return priority of the device + * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); + return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); + } + + /** + * Get the connection policy of the profile. + * + *

The connection policy can be any of: + * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, + * {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Bluetooth device + * @return connection policy of the device + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getConnectionPolicy(BluetoothDevice device) { + if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getPriority(device); + return service.getConnectionPolicy(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); - return PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } } if (service == null) Log.w(TAG, "Proxy not attached to service"); - return PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } /** -- cgit v1.2.3 From 3625be4eb7d52fe10c659b649d945a4cea0d0beb Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Tue, 10 Dec 2019 17:47:52 +0000 Subject: Use new UnsupportedAppUsage annotation. Existing annotations in libcore/ and frameworks/ will deleted after the migration. This also means that any java library that compiles @UnsupportedAppUsage requires a direct dependency on "unsupportedappusage" java_library. Bug: 145132366 Test: m && diff unsupportedappusage_index.csv Change-Id: I6ab53570aca580fbee1fcc927871caa09780f58f Merged-In: I6ab53570aca580fbee1fcc927871caa09780f58f --- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0ec473c85a..0aa5aac5d8 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -19,8 +19,8 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.Uri; import android.os.Binder; -- cgit v1.2.3 From 745310e2c9cdf886b71ffa7ad1b2404f9d0252f7 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 22 Jan 2020 14:26:35 -0800 Subject: Use @ConnectionPolicy annotation more consistently in classes with methods setConnectionPolicy and getConnectionPolicy Bug: 147669289 Test: Manual Change-Id: I2b9b0391a02d01623c1cd253f2da12b2baaea599 --- framework/java/android/bluetooth/BluetoothMapClient.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0aa5aac5d8..8d2aaddd38 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -271,7 +271,8 @@ public final class BluetoothMapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -319,7 +320,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- cgit v1.2.3 From c228ce27e2d7fe26193236eb5d4299214deb17ec Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 18 Mar 2020 17:46:33 -0700 Subject: Add BLUETOOTH_PRIVILEGED permission as a requirement for all new Bluetooth SystemApis and for hidden connect/disconnect APIs. Hide some APIs that were previously marked as @UnsupportedAppUsage and re-add annotation as changing the permissions for these SystemApis would break the unsupported app contract that was previously there. Therefore, we're choosing to hide them until we have a good story on how to deal with them next release. Bug: 148689314 Test: Manual Merged-In: I33ee2c7ccd3827db3d23d6447cf82d9ffc36836a Change-Id: I33ee2c7ccd3827db3d23d6447cf82d9ffc36836a --- .../java/android/bluetooth/BluetoothMapClient.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 8d2aaddd38..19240dc0bb 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.PendingIntent; @@ -140,7 +141,10 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Initiate connection. Initiation of outgoing connections is not * supported for MAP server. + * + * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); final IBluetoothMapClient service = getService(); @@ -162,7 +166,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @param device Remote Bluetooth Device * @return false on error, true otherwise + * + * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); final IBluetoothMapClient service = getService(); @@ -251,7 +258,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -270,8 +277,8 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMapClient service = getService(); @@ -301,7 +308,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -319,8 +326,8 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- cgit v1.2.3 From d9f7c771e6e7be6337879c5f3b72a6ec3272af8b Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 19 Jun 2020 14:26:01 -0700 Subject: Remove unnecessarily @System/TestApi annotations We can't expose APIs if the enclosing class is hidden, so these annotations are redundant. We need to remove them so that we can enable the check. Exempt-From-Owner-Approval: Cherry-pick from goog/master Bug: 159121253 Test: treehugger (i.e. this shouldn't trigger "API has changed" error.) Merged-in: Ie1841a670bdf6c6f4b25a1fc5deed8ec2d18cda2 Change-Id: I36e3562b72e64b51e4febd1d42a3bc8e4dc60988 --- framework/java/android/bluetooth/BluetoothMapClient.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 19240dc0bb..4f5c4feb36 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -276,7 +275,6 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -325,7 +323,6 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); -- cgit v1.2.3 From 7498830ee0cbfa567a672503fc9dd9ed381b38f9 Mon Sep 17 00:00:00 2001 From: Fei Zheng Date: Wed, 25 Dec 2019 13:53:16 +0800 Subject: Bluetooth: MCE: Add new API to set message read status or deleted status Bug: 146314855 Test: 1. Pair with a remote device which address is like 00:01:02:03:04:05 2. On remote device allow MAP connection 3. Make sure there is at least 1 unread message in last week on remote device 4. adb shell am instrument -w -e mce_set_message_status_iterations 1 -e device_address 00:01:02:03:04:05 -e class android.bluetooth.BluetoothStressTest#testMceSetMessageStatus com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner Change-Id: I7a3e337142bc39a55c1bfd425e0966e1fb1b9a68 --- .../java/android/bluetooth/BluetoothMapClient.java | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0aa5aac5d8..f01a05a14e 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -52,6 +52,18 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; + /** + * Action to notify read status changed + */ + public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = + "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; + + /** + * Action to notify deleted status changed + */ + public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = + "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; + /* Extras used in ACTION_MESSAGE_RECEIVED intent. * NOTE: HANDLE is only valid for a single session with the device. */ public static final String EXTRA_MESSAGE_HANDLE = @@ -65,6 +77,25 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; + /** + * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED + * Contains the MAP message deleted status + * Possible values are: + * true: deleted + * false: undeleted + */ + public static final String EXTRA_MESSAGE_DELETED_STATUS = + "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS"; + + /** + * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED + * Possible values are: + * 0: failure + * 1: success + */ + public static final String EXTRA_RESULT_CODE = + "android.bluetooth.device.extra.RESULT_CODE"; + /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -75,6 +106,12 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final int UPLOADING_FEATURE_BITMASK = 0x08; + /** Parameters in setMessageStatus */ + public static final int UNREAD = 0; + public static final int READ = 1; + public static final int UNDELETED = 2; + public static final int DELETED = 3; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, @@ -400,6 +437,38 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } + /** + * Set message status of message on MSE + *

+ * When read status changed, the result will be published via + * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED} + * When deleted status changed, the result will be published via + * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED} + * + * @param device Bluetooth device + * @param handle message handle + * @param status UNREAD for "unread", READ for + * "read", UNDELETED for "undeleted", DELETED for + * "deleted", otherwise return error + * @return true if request has been sent, false on error + * + */ + @RequiresPermission(Manifest.permission.READ_SMS) + public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { + if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")"); + final IBluetoothMapClient service = getService(); + if (service != null && isEnabled() && isValidDevice(device) && handle != null && + (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) { + try { + return service.setMessageStatus(device, handle, status); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; -- cgit v1.2.3 From 049f0f5f4196aa3c1897bcbfd1a86c66ab1affcc Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 4 Nov 2020 09:29:36 +0000 Subject: Add maxTargetSdk restriction to unused APIs. These are APIs that have @UnsupportedAppUsage but for which we don't have any evidence of them currently being used, so should be safe to remove from the unsupported list. Bug: 170729553 Test: Treehugger Merged-In: I626caf7c1fe46c5ab1f39c2895b42a34319f771a Change-Id: I54e5ecd11e76ca1de3c5893e3a98b0108e735413 --- framework/java/android/bluetooth/BluetoothMapClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index df11d3adac..ff6cffb272 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -24,6 +24,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -388,7 +389,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @param deliveredIntent intent issued when message is delivered * @return true if the message is enqueued, false on error */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); -- cgit v1.2.3 From 1db9bd4e9a640c133d121f1d0a017059be2086a3 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 21 Jan 2021 11:22:44 -0800 Subject: Add BluetoothMapClient to system API Add BluetoothMapClient.sendMessage to system API for use by mainline modules. Bug: 157948464 Test: atest BluetoothInstrumentationTests Tag: #feature Change-Id: Idee4cb2cefeaf03f0351ea576f919a294219d391 --- .../java/android/bluetooth/BluetoothMapClient.java | 101 ++++++++++++++++++--- 1 file changed, 86 insertions(+), 15 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ff6cffb272..0312a2190a 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -18,7 +18,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -30,6 +32,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -37,44 +40,60 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothMapClient implements BluetoothProfile { private static final String TAG = "BluetoothMapClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); + /** @hide */ public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; + /** @hide */ public static final String ACTION_MESSAGE_RECEIVED = "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; /* Actions to be used for pending intents */ + /** @hide */ public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; + /** @hide */ public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; /** * Action to notify read status changed + * + * @hide */ public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; /** * Action to notify deleted status changed + * + * @hide */ public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; - /* Extras used in ACTION_MESSAGE_RECEIVED intent. - * NOTE: HANDLE is only valid for a single session with the device. */ + /** + * Extras used in ACTION_MESSAGE_RECEIVED intent. + * NOTE: HANDLE is only valid for a single session with the device. + */ + /** @hide */ public static final String EXTRA_MESSAGE_HANDLE = "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; + /** @hide */ public static final String EXTRA_MESSAGE_TIMESTAMP = "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP"; + /** @hide */ public static final String EXTRA_MESSAGE_READ_STATUS = "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS"; + /** @hide */ public static final String EXTRA_SENDER_CONTACT_URI = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; + /** @hide */ public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; @@ -84,6 +103,8 @@ public final class BluetoothMapClient implements BluetoothProfile { * Possible values are: * true: deleted * false: undeleted + * + * @hide */ public static final String EXTRA_MESSAGE_DELETED_STATUS = "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS"; @@ -93,24 +114,42 @@ public final class BluetoothMapClient implements BluetoothProfile { * Possible values are: * 0: failure * 1: success + * + * @hide */ public static final String EXTRA_RESULT_CODE = "android.bluetooth.device.extra.RESULT_CODE"; - /** There was an error trying to obtain the state */ + /** + * There was an error trying to obtain the state + * @hide + */ public static final int STATE_ERROR = -1; + /** @hide */ public static final int RESULT_FAILURE = 0; + /** @hide */ public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ + /** + * Connection canceled before completion. + * @hide + */ public static final int RESULT_CANCELED = 2; - + /** @hide */ private static final int UPLOADING_FEATURE_BITMASK = 0x08; - /** Parameters in setMessageStatus */ + /* + * UNREAD, READ, UNDELETED, DELETED are passed as parameters + * to setMessageStatus to indicate the messages new state. + */ + + /** @hide */ public static final int UNREAD = 0; + /** @hide */ public static final int READ = 1; + /** @hide */ public static final int UNDELETED = 2; + /** @hide */ public static final int DELETED = 3; private BluetoothAdapter mAdapter; @@ -132,19 +171,12 @@ public final class BluetoothMapClient implements BluetoothProfile { mProfileConnector.connect(context, listener); } - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - /** * Close the connection to the backing service. * Other public functions of BluetoothMap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * @hide */ public void close() { mProfileConnector.disconnect(); @@ -158,6 +190,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Returns true if the specified Bluetooth device is connected. * Returns false if not connected, or if this proxy object is not * currently connected to the Map service. + * @hide */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); @@ -225,6 +258,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the list of connected devices. Currently at most one. * * @return list of connected devices + * @hide */ @Override public List getConnectedDevices() { @@ -246,6 +280,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the list of devices matching specified states. Currently at most one. * * @return list of matching devices + * @hide */ @Override public List getDevicesMatchingConnectionStates(int[] states) { @@ -267,6 +302,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get connection state of device * * @return device connection state + * @hide */ @Override public int getConnectionState(BluetoothDevice device) { @@ -378,6 +414,38 @@ public final class BluetoothMapClient implements BluetoothProfile { } /** + * Send a message. + * + * Send an SMS message to either the contacts primary number or the telephone number specified. + * + * @param device Bluetooth device + * @param contacts Uri Collection of the contacts + * @param message Message to be sent + * @param sentIntent intent issued when message is sent + * @param deliveredIntent intent issued when message is delivered + * @return true if the message is enqueued, false on error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.SEND_SMS) + public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection contacts, + @NonNull String message, @Nullable PendingIntent sentIntent, + @Nullable PendingIntent deliveredIntent) { + if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); + final IBluetoothMapClient service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]), + message, sentIntent, deliveredIntent); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + /** * Send a message. * * Send an SMS message to either the contacts primary number or the telephone number specified. @@ -388,6 +456,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @param sentIntent intent issued when message is sent * @param deliveredIntent intent issued when message is delivered * @return true if the message is enqueued, false on error + * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, @@ -410,6 +479,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @param device Bluetooth device * @return true if the message is enqueued, false on error + * @hide */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); @@ -431,6 +501,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @param device The Bluetooth device to get this value for. * @return Returns true if the Uploading bit value in SDP record's * MapSupportedFeatures field is set. False is returned otherwise. + * @hide */ public boolean isUploadingSupported(BluetoothDevice device) { final IBluetoothMapClient service = getService(); @@ -457,7 +528,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * "read", UNDELETED for "undeleted", DELETED for * "deleted", otherwise return error * @return true if request has been sent, false on error - * + * @hide */ @RequiresPermission(Manifest.permission.READ_SMS) public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { -- cgit v1.2.3 From 8f80e4a05b3f1b227f40de5ec0e9a6297154ffc0 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 2 Apr 2021 08:06:09 -0600 Subject: Update Bluetooth API annotations. Recent work has introduced a new "Nearby devices" runtime permission which protects all existing Bluetooth APIs; we've done this by defining a to convert the old BLUETOOTH and BLUETOOTH_ADMIN permissions into one of three new permissions: * BLUETOOTH_ADVERTISE: Required to be able to advertise to nearby Bluetooth devices. * BLUETOOTH_CONNECT: Allows applications to connect to paired bluetooth devices. * BLUETOOTH_SCAN: Required to be able to discover and pair nearby Bluetooth devices. At its core, this change begins updating the Bluetooth APIs to have correct @RequiresPermission indicating which permission is actually enforced internally. To ensure alignment across Binder, the newly added "RequiresPermissionChecker" Error Prone checker was used to discover any inconsistencies, ensuring correctness from server-side enforcement up through to the public APIs. In addition, since developers will continue building apps for both modern and legacy platforms, this change introduces new auto-doc annotations which will emit helpful consistent documentation describing the behavior of older devices that are still using the old permission model. Bug: 183626724 Test: ./build/soong/soong_ui.bash --make-mode Bluetooth RUN_ERROR_PRONE=true Change-Id: I02aa127e8e07f239561f4f2a3bbdfc6fccb82f7f --- .../java/android/bluetooth/BluetoothMapClient.java | 53 ++++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0312a2190a..db74a90f60 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -192,6 +192,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * currently connected to the Map service. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); final IBluetoothMapClient service = getService(); @@ -214,7 +215,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); final IBluetoothMapClient service = getService(); @@ -239,7 +243,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); final IBluetoothMapClient service = getService(); @@ -261,6 +268,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); final IBluetoothMapClient service = getService(); @@ -283,6 +291,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); final IBluetoothMapClient service = getService(); @@ -305,6 +314,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); final IBluetoothMapClient service = getService(); @@ -331,7 +341,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -349,7 +362,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -380,7 +396,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -397,7 +416,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); final IBluetoothMapClient service = getService(); @@ -427,7 +449,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.SEND_SMS) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.SEND_SMS, + }) public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection contacts, @NonNull String message, @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveredIntent) { @@ -459,6 +484,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.SEND_SMS, + }) public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); @@ -481,6 +510,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if the message is enqueued, false on error * @hide */ + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.READ_SMS, + }) public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); final IBluetoothMapClient service = getService(); @@ -503,6 +536,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * MapSupportedFeatures field is set. False is returned otherwise. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isUploadingSupported(BluetoothDevice device) { final IBluetoothMapClient service = getService(); try { @@ -530,7 +564,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if request has been sent, false on error * @hide */ - @RequiresPermission(Manifest.permission.READ_SMS) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.READ_SMS, + }) public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")"); final IBluetoothMapClient service = getService(); -- cgit v1.2.3 From 5ba8bfca7e9adf5c6d8ee8180aebad6f04037d6c Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 16 Apr 2021 09:53:23 -0600 Subject: More Bluetooth API annotation updates. This change adds a "BluetoothPermissionChecker" that ensures that all Bluetooth permission annotations are consistent. In addition, it verifies that all Bluetooth public APIs have been audited to be permission protected where relevant. We've currently standardized on saying that APIs that return device or Bluetooth state information (without sharing details about any particular remote Bluetooth device) do not need to be permission protected. This change is only annotations and has no behavior changes. Bug: 183626724 Test: ./build/soong/soong_ui.bash --make-mode Bluetooth RUN_ERROR_PRONE=true Change-Id: Ie80b15b058359bf1e9a6ee881b89cb3e5b584ca1 --- .../java/android/bluetooth/BluetoothMapClient.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index db74a90f60..f20b533af0 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -20,8 +20,10 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.PendingIntent; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.Uri; @@ -192,6 +194,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * currently connected to the Map service. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); @@ -215,6 +218,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -243,6 +247,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -268,6 +273,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); @@ -291,6 +297,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); @@ -314,6 +321,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); @@ -341,6 +349,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -362,6 +371,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -396,6 +406,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return priority of the device * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -416,6 +427,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -449,6 +461,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.SEND_SMS, @@ -484,6 +497,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.SEND_SMS, @@ -510,6 +524,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if the message is enqueued, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.READ_SMS, @@ -536,6 +551,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * MapSupportedFeatures field is set. False is returned otherwise. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isUploadingSupported(BluetoothDevice device) { final IBluetoothMapClient service = getService(); @@ -564,6 +580,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if request has been sent, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.READ_SMS, -- cgit v1.2.3 From d7c55664839ca7e6237cd0ef0f36eb51a4ee7ae6 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 20 Apr 2021 12:30:37 -0600 Subject: Annotations for Bluetooth broadcast intents. Recent work has been using Error Prone rules and annotations to reflect the current state of permission enforcement across the Bluetooth stack, and we're now in a position were we can add new permission enforcement that had been missing. We've currently standardized on saying that APIs that return device or Bluetooth state information (without sharing details about any particular remote Bluetooth device) do not need to be permission protected. Bug: 183626724 Test: ./build/soong/soong_ui.bash --make-mode Bluetooth RUN_ERROR_PRONE=true Change-Id: I53ac7a4fe1dea57316048c3cac4fa237b6ba3d38 --- .../java/android/bluetooth/BluetoothMapClient.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index f20b533af0..d72081c0ba 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -20,8 +20,10 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -50,16 +52,27 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; /** @hide */ + @RequiresPermission(android.Manifest.permission.RECEIVE_SMS) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_RECEIVED = "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; /* Actions to be used for pending intents */ /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; @@ -68,6 +81,9 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; @@ -76,6 +92,9 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; -- cgit v1.2.3 From f9e176c3dcafb82f251a123751578539e3484deb Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 22 Apr 2021 16:01:29 -0600 Subject: More AttributionSource plumbing. To prepare for future work which will plumb AttributionSource values through all remaining AIDLs, we need profiles to interact directly with the specific BluetoothAdapter they were created from. This is how we'll ensure that the relevant AttributionSource can be chained down from the original Context they're obtained from. This change also marks getDefaultAdapter() as deprecated to clearly communicate that BluetoothManager.getAdapter() is the best-practice path to obtaining a correctly scoped BluetoothAdapter instance. Bug: 183626112 Test: atest BluetoothInstrumentationTests Change-Id: I1e15170d7679019bbb6e396279d6e633e3dad4d6 --- .../java/android/bluetooth/BluetoothMapClient.java | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index d72081c0ba..823967de6d 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -27,6 +27,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.net.Uri; import android.os.Binder; @@ -173,7 +174,8 @@ public final class BluetoothMapClient implements BluetoothProfile { /** @hide */ public static final int DELETED = 3; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, "BluetoothMapClient", IBluetoothMapClient.class.getName()) { @@ -186,9 +188,11 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Create a BluetoothMapClient proxy object. */ - /*package*/ BluetoothMapClient(Context context, ServiceListener listener) { + /* package */ BluetoothMapClient(Context context, ServiceListener listener, + BluetoothAdapter adapter) { if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -299,7 +303,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -323,7 +328,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -620,14 +626,10 @@ public final class BluetoothMapClient implements BluetoothProfile { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; - if (DBG) Log.d(TAG, "Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } -- cgit v1.2.3 From 43ee69eed974cd7ebc4784416a6e1e251464cc36 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 23 Apr 2021 14:13:57 -0600 Subject: Long-tail of AttributionSource plumbing. Wires up AttributionSource across the remaining long-tail of Bluetooth AIDL interfaces, ensuring that developers can accurately make calls chained back to a specific Context. Moves "for data delivery" permission checks to happen in a single location on each interface to ensure they're performed consistently with the new AttributionSource arguments. Note that "for data delivery" isn't the best name; it's designed to represent that the requested action was performed and should result in the relevant appop being noted for the caller. This change has the positive side effect of ensuring that all interfaces are consistently enforcing the BLUETOOTH_CONNECT permission, even in the case where BLUETOOTH_PRIVILEGED is also required; this is what ensures that revoking the "Nearby devices" permission takes effect for all callers. Additionally, standardizing on enforcing permissions closer to the AIDL entry point reduces the need for @RequiresPermission annotations to be carried around inside the Bluetooth stack. Bug: 183626112 Test: atest BluetoothInstrumentationTests Change-Id: I8023dda654e325b8bfa2f0cdb994ad63a2b429d4 --- .../java/android/bluetooth/BluetoothMapClient.java | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 823967de6d..14804dbefe 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -21,9 +21,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -224,7 +223,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null) { try { - return service.isConnected(device); + return service.isConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -251,7 +250,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -280,7 +279,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -304,7 +303,7 @@ public final class BluetoothMapClient implements BluetoothProfile { if (service != null && isEnabled()) { try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -329,7 +328,8 @@ public final class BluetoothMapClient implements BluetoothProfile { if (service != null && isEnabled()) { try { return BluetoothDevice.setAttributionSource( - service.getDevicesMatchingConnectionStates(states), mAttributionSource); + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -353,7 +353,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -411,7 +411,7 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -462,7 +462,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -499,7 +499,7 @@ public final class BluetoothMapClient implements BluetoothProfile { if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]), - message, sentIntent, deliveredIntent); + message, sentIntent, deliveredIntent, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -533,7 +533,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); + return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -559,7 +560,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getUnreadMessages(device); + return service.getUnreadMessages(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -582,7 +583,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); try { return (service != null && isEnabled() && isValidDevice(device)) - && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + && ((service.getSupportedFeatures(device, mAttributionSource) + & UPLOADING_FEATURE_BITMASK) > 0); } catch (RemoteException e) { Log.e(TAG, e.getMessage()); } @@ -616,7 +618,7 @@ public final class BluetoothMapClient implements BluetoothProfile { if (service != null && isEnabled() && isValidDevice(device) && handle != null && (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) { try { - return service.setMessageStatus(device, handle, status); + return service.setMessageStatus(device, handle, status, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; -- cgit v1.2.3 From 98f3044ce87c7ab9d2a0efbfb8ef6a16872262df Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 3 Jun 2021 09:26:53 -0600 Subject: More Binder call AttributionSource assignment. Since developers can use a BluetoothDevice object can make remote calls, it needs to have an accurate AttributionSource. Previous CLs had updated many places where these BluetoothDevice instances were passed across Binder interfaces, but this change updates several remaining locations which had been missed. Introduces new "Attributable" marker interface to offer consistent tooling when applying AttributionSource updates. Bug: 187097694 Test: atest BluetoothInstrumentationTests Change-Id: Icad3b9726591f0fbad58a493cefa5a0af7648280 --- framework/java/android/bluetooth/BluetoothMapClient.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 14804dbefe..042b58669a 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.net.Uri; @@ -302,7 +303,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); @@ -327,7 +328,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { -- cgit v1.2.3 From d517127a57b499305f82ec6eea6da8e0bd485e87 Mon Sep 17 00:00:00 2001 From: William Escande Date: Tue, 14 Dec 2021 16:16:11 +0100 Subject: Copy attributable to Bluetooth Attributable is called by bluetooth and it's hidden. By copying into bluetooth we are now allowed to call it Bug: 210467788 Test: build Tag: #refactor Change-Id: I73ea07c9439988ab5477c82799f718c6d81513be --- framework/java/android/bluetooth/BluetoothMapClient.java | 1 - 1 file changed, 1 deletion(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 042b58669a..8a3f80164a 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -26,7 +26,6 @@ import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.net.Uri; -- cgit v1.2.3 From b86023dde0fbbe539e2415942ffe1c97108654f8 Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 16 Dec 2021 16:07:55 +0100 Subject: Remove allowBlocking from all BluetoothProfiles Since Bluetooth is becoming a mainline module, it can no longer call the allowBlocking hidden api. Instead, all interface are moved to be oneway and use a synchronous data to handle the return value. Bug: 200200870 Test: Build + start Bt and play something on speaker Tag: #refactor Merged-In: I776a6322faadca1504bce24f2b6b041e756b6448 Change-Id: I776a6322faadca1504bce24f2b6b041e756b6448 --- .../java/android/bluetooth/BluetoothMapClient.java | 251 ++++++++++++--------- 1 file changed, 150 insertions(+), 101 deletions(-) (limited to 'framework/java/android/bluetooth/BluetoothMapClient.java') diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 8a3f80164a..03536f9aad 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,15 +31,17 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; import android.net.Uri; -import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.TimeoutException; /** * This class provides the APIs to control the Bluetooth MAP MCE Profile. @@ -180,7 +184,7 @@ public final class BluetoothMapClient implements BluetoothProfile { "BluetoothMapClient", IBluetoothMapClient.class.getName()) { @Override public IBluetoothMapClient getServiceInterface(IBinder service) { - return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothMapClient.Stub.asInterface(service); } }; @@ -221,17 +225,20 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); final IBluetoothMapClient service = getService(); - if (service != null) { - try { - return service.isConnected(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + final boolean defaultValue = false; + if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isConnected(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return false; + return defaultValue; } /** @@ -248,17 +255,20 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); final IBluetoothMapClient service = getService(); - if (service != null) { - try { - return service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + final boolean defaultValue = false; + if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.connect(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return false; + return defaultValue; } /** @@ -277,15 +287,20 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.disconnect(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return defaultValue; } /** @@ -300,17 +315,23 @@ public final class BluetoothMapClient implements BluetoothProfile { public List getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getConnectedDevices(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getConnectedDevices(mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return new ArrayList<>(); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), + mAttributionSource); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList<>(); + return defaultValue; } /** @@ -325,18 +346,23 @@ public final class BluetoothMapClient implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); return Attributable.setAttributionSource( - service.getDevicesMatchingConnectionStates(states, mAttributionSource), + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return new ArrayList<>(); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList<>(); + return defaultValue; } /** @@ -351,16 +377,20 @@ public final class BluetoothMapClient implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; + final SynchronousResultReceiver recv = new SynchronousResultReceiver<>(); + service.getConnectionState(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.STATE_DISCONNECTED; + return defaultValue; } /** @@ -405,20 +435,22 @@ public final class BluetoothMapClient implements BluetoothProfile { @ConnectionPolicy int connectionPolicy) { if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { try { - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return defaultValue; } /** @@ -460,16 +492,20 @@ public final class BluetoothMapClient implements BluetoothProfile { public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getConnectionPolicy(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return defaultValue; } /** @@ -494,18 +530,8 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection contacts, @NonNull String message, @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveredIntent) { - if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); - final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]), - message, sentIntent, deliveredIntent, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; - } - } - return false; + return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent, + deliveredIntent); } /** @@ -531,16 +557,21 @@ public final class BluetoothMapClient implements BluetoothProfile { PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendMessage(device, contacts, message, sentIntent, deliveredIntent, + mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - return false; + return defaultValue; } /** @@ -558,15 +589,20 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getUnreadMessages(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getUnreadMessages(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - return false; + return defaultValue; } /** @@ -580,13 +616,21 @@ public final class BluetoothMapClient implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isUploadingSupported(BluetoothDevice device) { + if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")"); final IBluetoothMapClient service = getService(); - try { - return (service != null && isEnabled() && isValidDevice(device)) - && ((service.getSupportedFeatures(device, mAttributionSource) - & UPLOADING_FEATURE_BITMASK) > 0); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); + final int defaultValue = 0; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getSupportedFeatures(device, mAttributionSource, recv); + return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue) + & UPLOADING_FEATURE_BITMASK) > 0; + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } return false; } @@ -615,16 +659,21 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")"); final IBluetoothMapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device) && handle != null && - (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ + || status == UNREAD || status == UNDELETED || status == DELETED)) { try { - return service.setMessageStatus(device, handle, status, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setMessageStatus(device, handle, status, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - return false; + return defaultValue; } private boolean isEnabled() { -- cgit v1.2.3